0X00 漏洞描述
CVE-2013-2551是由于负责VML解析的模块VGX.DLL,在处理<v:stroke>标签的dashstyle.array.length属性时,没有对传入的参数进行完备验证而导致的整数溢出。攻击者利用这个漏洞能够对任意地址进行读写操作——通过读取敏感内存信息、改写对象虚函数表,就能够完美绕过重重内存防护机制实现任意代码执行。
0X01 分析环境
目标系统:Windows 7 32位
调试器:WinDbg
反汇编器:IDA Pro
漏洞软件:IE8
0X02 基本信息
首先对IE开启堆页 老生常谈了 前面的博客都讲过了 然后打开IE 加载POC之后 允许阻止的内容
然后用WinDbg附加进程 点击crash! WinDbg断下 查看堆栈调用 可以看到是vgx!ORG::Get函数在调用memcpy复制数据时 源地址索引出错
查看vgx模块的详细信息
使用IDA加载VGX.dll然后搜索ORG::Get模块 查看反汇编代码 复制的源地址是vgx!ORG::Get的第一个参数
IDA的交叉引用也看不出来第一个参数的来源
我们回到WinDbg 可以看到上一层调用是vgx!COALineDashStyleArray::get_item函数
用IDA查看该函数反汇编代码 并看不出来什么 可以推断这个函数只是用来触发异常的 继续分析意义不大
所以我们从POC源码入手 查看body部分 主要用VML画出两个圆 分别设置id为vml1和shape 并对shape用dashstyle属性设置线条风格 关于dashstyle的各个属性定义可以参考:https://docs.microsoft.com/en-us/windows/win32/vml/msdn-online-vml-dashstyle-attribute
其中createRects函数代码如图 创建0x400个v:shape元素 用shape可以画出所需要的图形
单击poc的触发按钮后会调用crashme函数 该函数 首先获取前面0x400个shape元素的_vgRuntimeStyle属性 再通过它获取rotation属性值 然后设置vml1元素的dashstyle属性 长度为0x2c 后面设置的dashstyle数组长度时会与该值作比较
回到IDA 利用IDA快速找到JS函数对应IE类函数的方法 搜索框中输入“runtimestyle”可以找到COARuntimeStyle类 而且只有这一个 所以可以断定shape元素的_vgRuntimeStyle属性是由COARuntimeStyle类负责处理的
dashstyle类
put类 有点多 只找交集部分 有两个 我们只看vgx!COALineDashStyle::put_value 用来设置dashstyle属性
回到POC源码 看到接下来会设置dashstyle数组长度为-1 就是0xFFFFFFFF 导致整数溢出
IDA看到设置dashstyle数组的函数为COALineDashStyleArray::put_length
回到WinDbg动态跟踪dashstyle数组长度值的传递情况
1 | 0:015> g |
由于dashstyle数组长度改写成0xFFFF 而实际大小只有0x2C 同时前面在对当前数组长度和设置长度比较时 采用的是有符号比较 导致堆块未被重新分配 在最后一段js代码中 通过dashstyle.array.item对dashstyle数组进行索引取值时 就会调用COALineDashStyleArray::get_item函数获取数组元素 此时就可能导致数组越界访问 最后造成进程异常崩溃
0X03 漏洞利用
打开exp.html 会弹出提示框 然后用WinDbg附加进程 使用dd命令查看提示框的地址
第一个就是字符’a’ 回到exp中 看到有a的存在
a[]数组是_vgRuntimeStyle对象 而a[i].marginleFtt的值是’a’ 我们可以推测vml1.dashstyle.array.item(0x2E+0x16)函数读取到的是_vgRuntimeStyle.marginLeft 的地址
验证一下其在内存中的位置 输入命令后 我们查看最后一个05f0b078的地址 减去 444 是 vml1.dashstyle.array.item( 0x2E+0x16 ) 函数中的参数为 0x2e + 0x16 为 0x44 ,每一个又战四个字节,就是 0x444 ,就是我们申请的数组一开始的位置
05f0b078是数组开始的地址 一共有0x2c的长度 再往后三个是0x2e 再往下就是037018ac保存’a’的地址 从0x2e到037018ac有0x16的偏移
利用思路
1)通过构造0x400个COARuntimeStyle对象 在第0x301从0开始计算 因此需要在加1 创建包含44个元素的dashstyle数组 他会分配4*44=0xb0大小的ORG数组
2)当ORG数组与COARuntimeStyle对象相邻时 可以利用COARuntimeStyle对象偏移的字符串 相当于ORG数组的第0x2c+8/4+0x58/4=0x2e+0x16个元素 从而获得COARuntimeStyle.marginLeft字符串在内存中的位置
3)通过泄露固定地址偏移计算出 ntdll.dll 基址 在利用基址构造出ROP指令来调用 ntdll!ZwProtectVirtualMenory函数将shellcode地址设置为可读可写可执行权限 从而绕过DEP+ASLR保护
4)再次利用漏洞将vgx!CsafePtr虚表指针覆写掉 从而获得代码执行
劫持eip
打开exp1.html 用windbg附加进程
call调用的是一个地址 是ecx+8 ecx=0x00 查看eax 就是对象的地址 可以推测 ecx的值来源与eax 查看exp源码 可以推测ecx的值来自eax eax存放指向虚表指针的地址我们可以控制 vml1.dashstyle.array.item(6) 的值来控制 ecx 值,转到我们的执行流程,达到劫持 eip 的目的
获取shellcode内存的地址
使用堆喷射 申请空间把shellcode放到我们构造好的eip 然后进行rop就可以进行漏洞执行了 我们将开始exp.html的’a’字符改成shellcode
信息泄露
泄露rop的地址 用u SharedUserData!SystemCallStub 这是固定地址0x7dde0300 用来实现快速系统调用 用来泄露ntdll的基址
我们果然看到了是这个固定地址 查看内存中放的是一个值 再使用lmm指令查看ntdll的地址范围是77c50000~77d8c000 内存中的值就是在这个范围中 所以我们得到偏移77c970b0-77c50000=470b0 然后更改exploit的代码
构造rop
可以使人用ntdll里面的代码来构造rop指令 且 VirtualProtect 底层调用的就是 ntdll.dll 里面的 NtProtectVirtualMenory 函数改变内存的属性 通过下面的函数tab2uni得到rop 然后使用a[i].marginLeft=rop_chain得到rop指令的地址
0X04 补丁分析
我们用IDA的Bindiff插件找到打补丁后的COALineDashStyleArray::put_length函数是1993DB35
然后IDA加载打补丁后的VGX.dll 找到该函数 F5查看反汇编代码 与原来未打补丁的VGX.dll的COALineDashStyleArray::put_length函数进行对比发现在函数开始后增加了一个判断 判断插入代码的参数是否小于0 如果小于0直接终止代码 否则继续执行
0X05 经验总结
这次算是完成的实现了一次漏洞复现的过程 过程中还有些许不完美 其实对exp的代码理解并不是很透彻 对于自己编写exp还有很长的路要走
get到了些知识点 用ntdll固定地址偏移来获得基址 虽然之前学过类似的 利用固定地址偏移泄露 就是在pwn中 但在实际漏洞中还没用过
在调试中对于汇编代码的熟悉程度也很重要 要知道哪部分的代码大致在干什么 才能在动态调试的时候得到想要的值一类的 目前自己这方面能力比较薄弱