这次exploit_me本来很感兴趣的,可是好像日子选太好了啊
,都是聚会,结果B就没有去做了,A也就没必要提交了,偶的书啊。
其实我对shellcode并没有什么研究,也就马马虎虎跟过一个ani漏洞的shellcode,就一点皮毛,讲的不对,还望多多指正。
先来看看exploit_me_A溢出发生的函数
用hexrays方便的可以看到c代码是代码:@exploit__00401000: ;<= Procedure Start sub esp, 0C8h or ecx, 0FFFFFFFFh xor eax, eax lea edx, dword ptr [esp] push esi push edi mov edi, dword ptr [esp+0D4h] push 040904Ch ; ASCII "********************" ; 源字符串计数,不能含有null字符,否则即被截断 repne scas byte ptr es:[edi] ; edi=pszRecv not ecx ; nRecvLen sub edi, ecx mov eax, ecx mov esi, edi mov edi, edx shr ecx, 2 rep movs dword ptr es:[edi], dword ptr [esi] mov ecx, eax and ecx, 3 rep movs byte ptr es:[edi], byte ptr [esi] ……
明显,pszRecv没有被经过边界检查就拷给了szRecv,而szRecv仅仅只有200 bytes,当pszRecv中长度大于200时,即导致溢出发生代码:int exploit__00401000(const char* pszRecv) { char szRecv[200]; strcpy(szRecv,pszRecv); …… }
更正
可以确定溢出的条件:
pszRecv必须大于200字节才能溢出,且不能含null字符
构造shellcode
21世纪,shellcode什么最重要--通用性,地球人都知道
以下言论可能直接导致众人笑偶门外汉。
shellcode其实通用性非常局限,无论是暴力搜索kernel32 base还是peb取kernel32 base,最后还是要借助jmp ebx或者jmp esp,那就不得不硬编码,windows补丁如此最多。。。通用是不是有局限了呢(偶的半sp2中kernel32基地址就是0x4FAD0000)。
偶要求不是太高,有没有可以在xp全系列里通用的jmp esp或jmp ebx地址呢(包括任意不完全补丁的xp)?可能会有,但是目前为止还没有被统计出来吧,xp补丁无穷技术就是最好的shellcode防火墙![]()
要在xp全系列通用?开始有人要嘘了,还好,针对这个特定的exploit_me_A,确实可以做到!
偶们知道exe文件基地址在编译时就是指定的,默认是0x400000,不象dll,exe基地址在运行是也不会改变(说的有点心虚,高手快来指正),那exe文件中某个retn的地址是不是就是确定的呢
哈哈,好了,我们回顾下溢出发生的函数
pszRecv,就是他代码:int exploit__00401000(const char* pszRecv) { char szRecv[200]; strcpy(szRecv,pszRecv); …… }
“伟大的Recv指针,他继承了指针的光荣的传统,p-s-z-R-e-c-v,这一刻他灵魂附体!在这一刻!他不是一个人在战斗!他不是一个人!!”
我们可以放弃esp,放弃ebx,放弃abdce了!
当这个溢出函数被调用时,堆栈为
代码:Esp-> return Address pszRevc ……
,只要将return Address覆盖为exploit_me_A中任意一个retn地址!就控制eip流向shellcode了!
按照前面的利用方法,shellcode被构造成
< 200 bytes ><4 bytes>
| shellcode1 | pRetn |
retn就选溢出发生函数function_00401000的好了
retn地址最高字节为00,这样shellcode为代码:00401000 sub esp, 0C8 ...... 004010A4 retn
< 200 bytes ><4 bytes>
| shellcode1 |A4104000|
遇0截断,实际shellcode只有203字节
幸好,function_00401000调用处只有一个,覆盖的返回地址始终为0x00401255,最高字节亦始终为00
所以不怕返回地址最高字节没有覆盖到而导致控制eip失败。
留下一个问题,实际可执行的shellcode被限制成了200字节,因为不能覆盖pszRevc。代码:0040124B lea edx, dword ptr [esp+34] ; loc_40124B 0040124F push edx 00401250 call 00401000 00401255 add esp, 4 ......
突破200字节限制
向上查找红色句子--溢出的限制条件--pszRecv必须大于200字节才能溢出,且不能含null字符
把限制条件转换为利用条件吧
再回顾下溢出发生的函数
shellcode被执行实在pszRecv指向的堆栈中,而导致溢出覆盖的是在szRecv指向的堆栈中,代码:int exploit__00401000(const char* pszRecv) { char szRecv[200]; strcpy(szRecv,pszRecv); …… }
所以,只要从pszRecv拷到szRecv的shellcode满足溢出长度即可
这样,可以把把shellcode构造成这样
< 200 bytes > < 4 bytes > <512-204bytes>
|shellcode1...jmp $+4 | A4104000 | shellcode2 |
好了,突破可执行shellcode 200字节限制了
现在,shellcode扩展到512字节了,这个长度可以做很多事了代码:0040120B push eax ; /Flags => 0 0040120C rep stos dword ptr es:[edi] ; | 0040120E lea ecx, dword ptr [esp+38] ; | 00401212 push 200 ; |BufSize = 200 (512.) 00401217 push ecx ; |Buffer 00401218 push ebx ; |Socket 00401219 call dword ptr [<&WS2_32.#16>] ; \recv ...
512能不能突破?留给大家玩了
代码:附件里的shellcode我直接用了导入表 名称位于 exploit_, 条目 10 地址=00408024 区段=.rdata 类型=输入 (已知) 名称=KERNEL32.LoadLibraryA 名称位于 exploit_, 条目 11 地址=00408028 区段=.rdata 类型=输入 (已知) 名称=KERNEL32.ExitProcess 名称位于 exploit_, 条目 46 地址=004080B4 区段=.rdata 类型=输入 (已知) 名称=KERNEL32.GetProcAddress