远程溢出实验

远程溢出

【实验目的】

1、掌握远程溢出的基本原理;
2、掌握实现远程溢出的基本手段。

【实验环境】

同学A:运行server.exe
同学B:运行exploit.exe(命令格式:exploit.exe ipaddress 8888)
备注:同学A、B分别登录windows靶机,运行程序在D盘->攻防工具包->远程溢出

【实验预备知识点】

远程缓冲区溢出原理和本地缓冲区溢出原理一样,不同之处在于远程缓冲区溢出需要利用套接口进行远程连接。本小节通过一个例子来分析远程缓冲区溢出的工作原理和攻击过程。该例子分为两个部分:一个是有缓冲区溢出漏洞的服务程序,一个是针对该漏洞编写的利用程序。假设服务端程序运行的机器操作系统类型是linux,CPU类型是x86,而利用程序则运行在另外一个类似配置的linux/x86机器上运行,进行远程攻击。
有缓冲区溢出漏洞的服务端程序Vulnerable.c:

#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#define BUFFER_SIZE 1024
#define NAME_SIZE 2048
 
int handling(int c)
{
char buffer[BUFFER_SIZE], name[NAME_SIZE];
    int bytes;
    strcpy(buffer, "My name is: ");
       //将buffer内容即“My name is:”进行发送
    bytes = send(c, buffer, strlen(buffer), 0);
    if (bytes == -1)
    return -1;
       //接受信息
    bytes = recv(c, name, sizeof(name), 0);
    if (bytes == -1)
    return -1;
    sprintf(buffer, "Hello %s, nice to meet you! \r\n", name);
       //发送buffer内容即“Hello %s, nice to meet you!”
    bytes = send(c, buffer, strlen(buffer), 0);
    if (bytes == -1)
    return -1;
    return 0;
}
 
int main(int argc, char *argv[])
{
int s, c, cli_size;
struct sockaddr_in srv, cli;
if (argc != 2)
   {
            fprintf(stderr, "usage: %s port\n", argv[0]);
       return 1;
   }
       //创建套接口
   s = socket(AF_INET, SOCK_STREAM, 0);
       if (s == -1)
       {
           perror("socket() failed");
          return 2;
}
//设置服务器端的IP地址和端口号等参数
       srv.sin_addr.s_addr = INADDR_ANY;
       srv.sin_port = htons( (unsigned short int) atol(argv[1]));
srv.sin_family = AF_INET;
//绑定端口
       if (bind(s, &srv, sizeof(srv)) == -1)
       {
           perror("bind() failed");
          return 3;
}
//监听客户端的信号
       if (listen(s, 3) == -1)
       {
           perror("listen() failed");
          return 4;
       }
       for(;;)
{
       //接受来自客户端的连接请求
         c = accept(s, &cli, &cli_size);
          if (c == -1)
          {
               perror("accept() failed");
             return 5;
          }
          printf("client from %s", inet_ntoa(cli.sin_addr));
                     //调用handling(int c)函数
          if (handling(c) == -1)
          fprintf(stderr, "%s: handling() failed", argv[0]);
              //关闭套接字
          close(c);
            }
return 0;
   }
 

服务端程序包括两个函数:主函数int main(int argc, char *argv[])和子函数int handling(int c)。主函数主要完成套接口的创建、服务器参数的设置、端口号的绑定、客户端连接请求的监听,之后就进入循环接收客户端连接请求,再调用子函数handling(int c)进行数据的接收和发送处理;子函数首先发送一个字符串“My name is:”,之后等待接受对方发送过来的名字信息,最后将名字信息通过语句sprintf(buffer, "Hello %s, nice to meet you!\r\n ", name)组合成一个新的字符串再发送出去。该服务程序在正常运行的时候没有什么问题,但如果对方发送过来的名字信息太长的话,程序就会出错了!出错的原因就在于语句sprintf(buffer, "Hello %s, nice to meet you!\r\n ", name),因为sprintf()函数是不对边界进行检查的,当发送方法送过来的名字信息(字符串)超过了buffer数组的长度,就会导致缓冲区溢出。
利用该缓冲区溢出漏洞,可以编写一个远程的缓冲区溢出利用程序。如下是该利用程序Exploit.c:

#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
//绑定端口3789的Shellcode
char shellcode[] =
 
//standard offset (probably must be modified)
#define RET 0xbffff5ec

在这里插入图片描述
在这里插入图片描述

在利用程序Exploit.c里,主要功能有如下几个方面:根据实际情况选择植入代码(即Shellcode)的组成元素、按照一定的构造类型构造植入代码、发送植入代码。在本示例程序中,服务端程序是运行在linux/x86平台上,因此采用的Shellcode也是针对linux/x86平台的机器代码;此外考虑溢出缓冲区的大小是1024字节,构造的溢出字符串的长度也不能小于1024字节;必须知道溢出缓冲区的入口地址(或者地址范围),以便改写保存于栈中的函数返回地址而改变程序执行流程。当然,要对远程目标进行攻击,还必须知道目标的IP地址和端口号等。只有当攻击者了解了上述的这些必要的目标信息后,才可能根据这些信息编写相应的利用程序。
在Exploit.c程序里,目标缓冲区的大小为1024,加上保存于栈里的前帧地址4字节和函数返回地址4字节,总长度也不超过1400字节,因此溢出字符串的长度取为1064(即数组buffer的长度),该长度完全能够溢出目标缓冲区并改写函数返回地址;在构造溢出字符串时,选择了三种组成元素:N、S、R,其中N表示空操作符号NOP,在x86类型CPU里就是“0x90”;S表示shellcode;R表示目标缓冲区的入口地址。在构成溢出字符串的时候采用了NSR型的构造类型,即空操作符NOP放置在溢出字符串的最前面,shellcode放置在溢出字符串的中间,而入口地址R则放置在最后,操作代码如下:
memset(buffer, 0x90, 1064);
memcpy(buffer+1001-sizeof(shellcode) , shellcode, sizeof(shellcode));
for(i=1022; i < 1059; i+=4) { * ((int *) &buffer[i]) = RET; }
在溢出字符串的最后放置入口地址,其作用就是改写函数返回地址,从而改变程序执行流程,转向溢出缓冲区的入口地址,执行刚才植入的shellcode。在复制入口地址的时候一般也放置多个,其作用是提高改写函数返回地址的成功率;在溢出字符串的前面加入多个空操作符NOP,主要作用是提高程序跳转执行shellcode的成功率;Shellcode是溢出字符串的核心部分,完成攻击者设定的任务。
溢出字符串必须发送到目标机器,让其接收并执行才能达到攻击目的。在Exploit.c程序里,目标的IP地址和端口号是通过主函数命令行参数给出,再通过创建套接口、连接目标、发送溢出字符串等操作将溢出字符串发送到目标机器(见Exploit.c程序)。如果发送出去的shellcode按照预期被目标程序执行,那么该次攻击就成功了,攻击示意图如下图所示。
在这里插入图片描述
图 1植入代码溢出指令跳转执行示意图
防御策略。远程缓冲区溢出攻击是针对远程系统或者运行的软件的缓冲区溢出漏洞,攻击过程必须要发送恶意代码到远程目标系统上,因此,用户据此要给系统安装防火墙等防毒杀毒软件,这样可以将攻击者发送来的恶意代码进行拦截或查杀。此外,最关键的还是用户要及时更新自己的系统,以及系统里安装的软件,将远程缓冲区溢出漏洞打上补丁。

【实验内容】

远程缓冲区溢出漏洞非常普遍,也常常对系统造成很大的危害。
自从1988年的“Morris Worm”事件以来,越来越多的类似漏洞不断被发现。2008年10月24日凌晨,联想网御安全服务部攻防研究团队在监测系统安全状态过程中,发现Windows Server服务远程RPC栈溢出漏洞(MS08-067)。这是Windows操作系统下的Server服务在处理RPC请求过程中存在的一个严重漏洞,远程攻击者可以通过发送恶意RPC请求触发这个溢出,导致完全入侵用户系统,并以SYSTEM权限执行任意指令并获取数据,造成系统失窃及系统崩溃等严重问题。 此次发现的漏洞影响覆盖面广,会影响除Windows Server 2008 Core以外所有Windows系统,包括:Windows 2000/XP/Server 2003/Vista/Server 2008的各个版本,甚至还包括测试阶段的Windows 7 Pre-Beta,并且,由于漏洞存在于Windows系统默认开启的Server服务当中,且现已出现针对此漏洞的蠕虫Gimmiv.A,因此不排除大规模流行变种出现的可能性,对用户系统安全存在严重威胁。尽管目前微软公司已经发布相应补丁,但仍有部分用户因系统原因无法成功安装补丁而暴露在风险之下。
远程缓冲区溢出是指溢出行为发生在远程主机上,而非攻击者本主机。远程缓冲区溢出攻击实质就是一种入侵行为,主要是攻击者为了得到一个基本访问权限,如果被溢出的进程是以root运行的话,那么溢出后直接是rootshell权限,攻击者就具有root访问权限。另外,利用远程缓冲区溢出漏洞,也可以发起拒绝服务攻击。

【实验步骤】

1、在windows虚拟机A(此实验靶机简称A)上,点击开始,打开命令提示符窗口
2、找到路径“我的电脑>D盘>攻防工具包>远程溢出>server.exe”,将server.exe拖进命令提示符窗口,并按回车键。
在这里插入图片描述

图 2运行server.exe
3、 在另外一台windows虚拟机B(此实验简称B)上用exploit.exe对靶机进行远程溢出。(命令格式:exploit.exe ipaddress 8888)。
同样找到路径“我的电脑>D盘>攻防工具包>远程溢出> exploit.exe”,将exploit.exe拖进命令提示符窗口,并输入命令192.168.138.37(windows靶机A的IP)8888
在这里插入图片描述

图 3 exploit.exe对靶机进行远程溢出
4、 在B上运行exploit.exe后,回到A可以看到下图,溢出成功:
在这里插入图片描述

图 4远程溢出

【实验思考题】

  1. 缓冲区溢出的危害有哪些?
    答:缓冲区溢出是指当计算机向缓冲区填充数据时超出了缓冲区本身的容量,溢出的数据覆盖在合法数据上。
    危害有以下两点:
    1、程序崩溃,导致拒绝服务
    2、跳转并且执行一段恶意代码
    原因:造成缓冲区溢出的主要原因是程序中没有仔细检查用户输入。

  2. 我们可以如何利用jmp.egp指令实现缓冲区溢出?
    答:JMP ESP在函数返回返回指令处填入在dll中搜索的 jmp esp的源码,在函数退出时将会把返回值处地址填入到EIP(即下一句要执行的指令地址)中,即程序跳到jmp esp处的地址执行该处的指令,此时ESP指向的地址也变为存储函数返回指令+4位置处的地址。
    add esp,-X
    jmp esp
    第一条指令抬高了栈指针到shellcode之前。X代表shellcode起始地址与esp的偏移。如果shellcode从缓冲区起始位置开始,那么就是buffer的地址偏移。这里不使用sub esp,X指令主要是避免X的高位字节为0的问题,很多情况下缓冲区溢出是针对字符串缓冲区的,如果出现字节0会导致缓冲区截断,从而导致溢出失败。
    第二条指令就是跳转到shellcode的起始位置继续执行。(又是jmp esp!)

  3. 如何发现系统存在缓冲区溢出时, 我们可以如何实现缓冲区溢出攻击?
    答:取得机器的控制权甚至是最高权限,利用缓冲区溢出漏洞攻击root程序,执行类似“exec(sh)”的执行代码来获得root 的shell。需要完成两个任务,就是在程序的地址空间里安排适当的代码和通过适当的初始化寄存器和存储器,让程序跳转到安排好的地址空间执行。
    1) 在程序的地址空间里安排适当的代码,通过“植入法”的方式来完成,
    2) 控制程序转移到攻击代码的形式,缓冲区溢出漏洞攻击都是在寻求改变程序的执行流程,使它跳转到攻击代码,最为基本的就是溢出一个没有检查或者其他漏洞的缓冲区,这样做就会扰乱程序的正常执行次序。

4.有什么好的方法保护缓冲区免受缓冲区溢出的攻击和影响?
答:目前有四种基本的方法保护缓冲区免受缓冲区溢出的攻击和影响:
1)强制写正确的代码的方法;
2)通过操作系统使得缓冲区不可执行,从而阻止攻击者殖入攻击代码,这种方法有效地阻止了很多缓冲区溢出的攻击,但是攻击者并不一定要殖入攻击代码来实现缓冲区溢出的攻击,所以这种方法还是存在很多弱点的;
3)利用编译器的边界检查来实现缓冲区的保护,这个方法使得缓冲区溢出不可能出现,从而完全消除了缓冲区溢出的威胁,但是相对而言代价比较大;
4)在程序指针失效前进行完整性检查,这样虽然这种方法不能使得所有的缓冲区溢出失效,但它的确确阻止了绝大多数的缓冲区溢出攻击,而能够逃脱这种方法保护的缓冲区溢出也很难实现;

  1. 缓冲区溢出漏洞攻击方式有哪些?
    答:利用缓冲区溢出漏洞攻击root程序,大都通过执行类似“exec(sh)”的执行代码来获得root 的shell。有两种方式,就是在程序的地址空间里安排适当的代码和通过适当的初始化寄存器和存储器,让程序跳转到安排好的地址空间执行。

版权声明:本文为qq_43717119原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。