一个去年7月份左右的漏洞,漏洞出在windows的基础组件MSXML上,刚出来时由于尚无补丁,且利用稳定,so成为了网页挂马的利器。这个漏洞也被N多人分析过了。。。本文纯粹是基于学习目的。因为这个漏洞的起因类似于释放后重用类型的漏洞,还是有研究一下的价值的。。利用方式可以采用浏览器漏洞惯用的Heap spray技术,也可以顺便熟悉实战一下这个东西。。。PS:生涯早期拙作,轻拍。。。我纯粹是为了转正。。 直接运行poc,崩溃到windbg中, 看崩溃出的汇编代码,eax指向的地址很像是一个对象,mov ecx [eax],因为ecx通常用于thiscall调用协定,故这个语句很像是传递对象的首地址(即虚表指针)。所以根据这里可以大致推断这是由于错误的把攻击数据当成了对象。 这里我们需要搞清楚两个问题,1:这个攻击数据的源头。2为什么这些数据会复制到eax里面。首先先搞清第一个问题: 从这里开始向上顺藤摸瓜。 根据下列的栈回溯记录,首先大致看一下崩溃的函数的大致结构。 在IDA中, 除了知道有几个if结构外,貌似看不出什么特别有价值的东西。。。 OK,直接在IDA中找到崩溃点, 这里可以看到eax里面的数据来自与pvarg这个局部变量,pvarg这个局部变量被一个导入函数OLEAUT32!VariantInit初始化了 这个函数很简单,只是把pvarg的低2个字节初始化为0。这里看来这个函数似乎没用(--0) 根据eax指向的地址,在这里之前已经被填充了攻击数据。根据address命令,这些数据是 在栈上。 我们发现这个函数在这之前并没有初始化这个局部变量,其他的函数也不可能初始化这个函数栈帧里的局部变量,故这些栈上的数据应该是其他函数用完释放的,故这个漏洞是个类似Use-after-free的漏洞。分析到这里我们也可以根据栈回溯的记录去动态调试确定这些数据的来源,根据poc里的代码: <html> <script> var obj = null; obj = new ActiveXObject("MICROSOFT.XMLDOM"); var src = unescape("%u4141䅁"); while (src.length < 0x1002) src += src; src = "\\\\\\\\xxx" + src; src = src.substr(0, 0x1000 -10); var pic = document.createElement("img"); pic.src = src; pic.nameProp; obj.definition(1000,1000,1000); </script> </html> 这些\X41都是正常申请的。 因为这个漏洞类似与UAF型漏洞,故如果要弄清楚这个漏洞的成因,我们应该更关心为什么会重用栈上的数据而不是这些数据是怎么来的(如果关心漏洞的利用,需要关注数据来源)。 Ok,重新明确了思路后,我们想看一下MSDN中关于definition的定义:http://msdn.microsoft.com/en-us/library/windows/desktop/ms764733(v=vs.85).aspx 可见definition是作为属性的,而poc通过调用definition方法触发了漏洞,可见这个漏洞可能是由于本来作为属性的definition被当作方法调用产生的。 我们仔细分析以下这个msxml3!_dispatchImpl::InvokeHelper这个函数。 IDA按F5, 我们修改一下poc,在poc里作为属性调用definition,然后在windbg里对这个函数起始处下断点。 通过对比属性和方法两个poc中这个函数的流程,我们发现这个函数所走的流程是不同的。 通过上面f5出来的代码也可以看到函数是被一个if else分成了两个大部分,当definition作为方法调用时,走的是if为真的分支。 我们先看一下if的条件。 对于第一个条件: 当definition作为属性调用时,[EDI+8]等于0,当definition作为方法调用时,[EDI+8]等于1,这里决定了函数的大的分支走向。通过IDA里面的注释: 可以看到这个参数的名称为tagDISPPARAMS,通过测试可以确定这个是definition参数的个数,=0时即是definition作为属性调用的时候。 第二个条件: Invoke_methods这个参数是用过后续的函数调用来确定,我们在IDA里向下分析,就会发现这个参数作为index在switch结构的线性表结构里选择执行函数,故可以确定这个参数是用于选择方法的,这里它的值是0x17,最后会选择get_definition函数。这里不为空(ebx为0) 第三个 这个参数在definition作为属性调用时为0x02,方法调用时为0x01,具体用途不明。 第四个和第五个 我们在windbg中跟踪到这里,查看eax的为5de1f638这个是dll文件中数据段的地址,直接在IDA中看注释: 我们看到很多类似的内容,以下面的firstChild为例,发现偏移14h和16h的地方都是一样的2和9。通过查MSDN可以知道firstChild也是只作为属性来调用的,所以我们可以把poc里面的definition改成firstChild,看看是否也会触发漏洞。 通过测试,并没有触发漏洞。我们在firstChild函数get_firstChild返回后下断点。 在调用firstChild方法时,pvarg附近的数据是: 可见偏移8h的数据是0,然后下条语句” cmp eax,ebx”,因为eax为0,所以直接跳转到结尾结束了,没有执行”mov ecx,dword ptr [eax]”,这里可以推断出程序已经探测到firstChild只能作为属性调用,作为方法调用时直接退出了。 通过查找MSDN,我们发现childNodes既可以作为方法也可以作为属性调用。所以我们在windbg中看一下使用childNodes时pvarg附近的数据。 我们看到在pvarg偏移8的位置是一个堆地址。这个地址是有效的,他指向的内容是一个对象。 但是当我们使用definition时,这里的返回值既不是0也不是一个有效地址。其实按照firstChild的调用,可以猜测正常来说这个时候pvarg偏移8的位置应该是0的。 根据紧挨这个位置的代码,pvarg的地址是作为参数的。Esi+20h指向的函数是一个switch结构,根据invoke_methods参数来选择调用的函数,当使用firstChild参数时调用的是get_firstChild函数,使用definition时调用的是get_definition函数,调用的时候都把pvarg的地址作为参数传入了。可见问题的关键是get_firstChild函数(5dda5358)对pvarg做了正确的处理,而get_definition(5dda5d38)则没有。 所以接下来的重点是仔细分析一下get_definition函数,以及对比一下这两个函数的区别。 首先从这个函数的参数入手: 第一个参数:IXMLDOMNode是根据注释的名称得来的,具体的结构可以直接观察内存,如果存储的是DLL文件数据段的地址,则可以在IDA中查看注释。 经过分析的IXMLDOMNode对象结构: +0h IXMLDOMNode虚表指针 +4h DOMNode虚表指针 +8h dispatchinfo地址(?) +ch 某个类的虚表指针(?) +10h 14h (不明,method的id?) +14h 类的虚表指针(?) +1ch Node类的地址 第二个参数:pvarg 里面的值是\x41,是被前面的函数释放的局部变量。 通过对比get_definition和get_firstChild这两个函数的流程,我们可以发现在下图这个地方,get_definition函数没有把参数pvarg指向的地址清0是漏洞产生的根本原因。 Get_definition函数的流程: get_firstChild函数的流程: 从上图可以清晰的看出来,get_firstChild函数中标注的地方对传入的pvarg参数指向的地址做了清零的操作(ebx寄存器为0)。注:本帖由看雪论坛志愿者PEstone 重新将PDF整理排版,若和原文有出入,以原作者附件为准
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!