继续学windbg
![](/view/img/face/12.gif)
,参考了坛子里的两篇文章,但是侧重点不太一样
本机环境为win xp sp3+IE6,mshtml版本为6.0.2900.5726,poc样本来自metasploit->CVE-2010-3962。
Metasploit上对该漏洞的简述为:mshtml在解析某特定CSS Tag时,有可能发生内存错误,进而导致某次调用变成[vTable+0x30+1],此漏洞能否利用取决于[vTable+0x30+1]处取出的地址能否被heapspray覆盖,因为不能操纵eip,所以此漏洞无法使用ROP来bypass DEP云云。
Poc分为两部分,一部分为heapspray,此处略过,另一部分是引发漏洞的那个特制的CSS Tag,如下
<table style=position:absolute;clip:rect(0)>
剥离heapspray部分,直接解析该tag,crash信息如下
(950.274): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=7e2233e1 ebx=0012d6a0 ecx=0273b6e0 edx=3fffffff esi=00000000 edi=0273b6e0
eip=147e27c9 esp=0012d654 ebp=0012d664 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
<Unloaded_erify.dll>+0x147e27c8:
147e27c9 ?? ???
0:000> k
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012d650 7e291a95 <Unloaded_erify.dll>+0x147e27c8
0012d664 7e2c61df mshtml!CLayout::EnsureDispNodeBackground+0x97
0012d728 7e2c5440 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x388
0012d908 7e2c5c25 mshtml!CTableLayout::CalculateLayout+0x295
0012da58 7e28b790 mshtml!CTableLayout::CalcSizeVirtual+0x665
0012db6c 7e2b9ec5 mshtml!CLayout::CalcSize+0x224
0012dbe0 7e2baa11 mshtml!CFlowLayout::MeasureSite+0x1e5
0012dc24 7e2ba948 mshtml!CFlowLayout::GetSiteWidth+0x12b
eip已经指向无效地址,反汇编崩溃之前的最后现场
0:000> u 7e291a90
mshtml!CLayout::EnsureDispNodeBackground+0x92:
7e291a90 8bcf mov ecx,edi
7e291a92 ff5030 call [COLOR="Red"]dword ptr [eax+30h][/COLOR] ;CRASH!!!
7e291a95 85c0 test eax,eax
7e291a97 7587 jne CLayout::EnsureDispNodeBackground+0x9b
7e291a99 8b8380000000 mov eax,dword ptr <Unloaded_.dll>+0x7f (00000080)[ebx]
7e291a9f c1e803 shr eax,3
7e291aa2 6a00 push 0
7e291aa4 83e001 and eax,1
从反汇编信息及metasploit上给出的简单描述猜测,可能就是7e291a92处的虚函数调用导致crash,查看一下虚表
0:000> dd eax
7e2233e1 e47e2a60 837e28ad d47e4140 dd7e2bdf
7e2233f1 c27e2e84 4d7e4140 1d7e2cf6 817e2b5f
7e223401 b97e2bdc 207e2bce 267e2ef5 147e27ce
7e223411 147e27c9 147e27c9 e17e27c9 a27e28fa
7e223421 267e2919 d97e2b40 707e28a7 687e2cff
7e223431 3f7e28b8 067e2e95 227e28b0 9e7e2d2f
7e223441 537e2b99 61007400 6b006300 6f002000
7e223451 65007600 66007200 6f006c00 00007700
其中0x30偏移处确实指向崩溃时的无效值0x147e27c9,事实上所有的虚函数均指向无效地址,甚至连虚表地址都是个未对齐值。据此猜测是虚表指针被意外修改(也有可能是虚函数地址被破坏,但可能性较小)。
结合metasploit的简述,查看虚表指针-1偏移处
0:000> dd eax-1
7e2233e0 7e2a6013 7e28ade4 7e414083 7e2bdfd4
7e2233f0 7e2e84dd 7e4140c2 7e2cf64d 7e2b5f1d
7e223400 7e2bdc81 7e2bceb9 7e2ef520 7e27ce26
7e223410 7e27c914 7e27c914 7e27c914 7e28fae1
7e223420 7e2919a2 7e2b4026 7e28a7d9 7e2cff70
7e223430 7e28b868 7e2e953f 7e28b006 7e2d2f22
7e223440 7e2b999e 00740053 00630061 0020006b
7e223450 0076006f 00720065 006c0066 0077006f
虚表指针-1处指向的确实是正确的虚表,下一步找出何时被修改,被谁修改,如果可以的话也应该搞清楚为什么会被修改。
鉴于堆管理的随机性,对于对象生老病死的跟踪比较麻烦,好在有symbols,结合ida,对CLayout::EnsureDispNodeBackground函数进行分析,此函数共有两个参数,参数1为CDispNodeInfo对象指针,记为pCDispNodeInfo,参数2为CDispNode对象指针,记为pCDispNode,部分反汇编如下
.text:7E291A4E mov edi, [ebp+pDispNode] ;参数2
.text:7E291A51 test edi, edi
.text:7E291A53 mov esi, ecx
.text:7E291A55 jz loc_7E32D97B
;------------------------------此处略过部分------------------------------
.text:7E291A87 mov ecx, edi
.text:7E291A89 call CDispNode::SetBackground(int)
.text:7E291A8E mov eax, [edi];eax = pDispNode -> vTable
.text:7E291A90 mov ecx, edi ;ecx = pDispNode -> this
.text:7E291A92 call dword ptr [eax+30h] ; CRASH!!!!
以上反汇编说明,被修改虚表指针的对象是一个CDispNode对象,并且在崩溃的时候edi为该对象指针,这一点很重要
这里用一个很土鳖的办法,首先在该类的new重载函数的
返回处下日志断点,统计CDispNode对象的创建操作
ba e 1 7E291B73 ".printf \"CDispNode 0x%08x\n\",@eax;.echo;g"
0:014> g
ModLoad: 75bc0000 75c3d000 C:\WINDOWS\system32\jscript.dll
CDispNode 0x02739144
CDispNode 0x027391b0
CDispNode 0x0273b304
CDispNode [COLOR="red"]0x0273b350 [/COLOR]
(a84.eb4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=7e2233e1 ebx=0012d6a0 ecx=0273b350 edx=3fffffff esi=00000000 edi=[COLOR="red"]0273b350[/COLOR]
eip=147e27c9 esp=0012d654 ebp=0012d664 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
<Unloaded_erify.dll>+0x147e27c8:
147e27c9 ?? ???
崩溃发生是edi为对象指针,值为0x0273b350,而打印的日志表示第四次创建操作所创建的正是该对象,此值实际上没有意义,关键是第‘
四’次,重新运行程序,再次下上述断点
ba e 1 7E291B73
0:014> g
ModLoad: 75bc0000 75c3d000 C:\WINDOWS\system32\jscript.dll
Breakpoint 0 hit
eax=0273a9c4 ebx=0012e3d0 ecx=00000000 edx=00000070 esi=00000001 edi=0273a3a0
eip=7e291b73 esp=0012e344 ebp=0012e354 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDispNode::operator new+0x4d:
7e291b73 c20c00 ret 0Ch
0:000> g
Breakpoint 0 hit
eax=0273aa30 ebx=0012e3d0 ecx=00000000 edx=0000002c esi=0273a9c4 edi=0273a3a0
eip=7e291b73 esp=0012e344 ebp=0012e354 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDispNode::operator new+0x4d:
7e291b73 c20c00 ret 0Ch
0:000> g
Breakpoint 0 hit
eax=0273c1d4 ebx=0273a4a0 ecx=00000000 edx=0000005c esi=0273a4a0 edi=00000000
eip=7e291b73 esp=0012d654 ebp=0012d664 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDispNode::operator new+0x4d:
7e291b73 c20c00 ret 0Ch
0:000> g
Breakpoint 0 hit
eax=0273c220 ebx=0273a4a0 ecx=00000000 edx=00000048 esi=0273c1d4 edi=00000000
eip=7e291b73 esp=0012d654 ebp=0012d664 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDispNode::operator new+0x4d:
7e291b73 c20c00 ret 0Ch
连续断下四次后,第四次的eax即为那个会被改了虚表指针的对象(悲剧的对象),得此值为0x0273c220,在该对象虚表指针处下写入log断点,让windbg打印出足够的信息,省得每次都得重新来过
ba w 4 0273c220 ".printf \"\\nvTable pointer has changed to 0x%08x\\n\",poi(0273c220);dd poi(0273c220);.echo;kb 5;g"
运行后得日志如下
0:000> g
vTable pointer has changed to 0x7e2233e0
7e2233e0 7e2a6013 7e28ade4 7e414083 7e2bdfd4
7e2233f0 7e2e84dd 7e4140c2 7e2cf64d 7e2b5f1d
7e223400 7e2bdc81 7e2bceb9 7e2ef520 7e27ce26
7e223410 7e27c914 7e27c914 7e27c914 7e28fae1
7e223420 7e2919a2 7e2b4026 7e28a7d9 7e2cff70
7e223430 7e28b868 7e2e953f 7e28b006 7e2d2f22
7e223440 7e2b999e 00740053 00630061 0020006b
7e223450 0076006f 00720065 006c0066 0077006f
ChildEBP RetAddr Args to Child
0012d664 7e2c617f 0273a4ac 00000000 0012da2c mshtml!CDispContainer::New+0x2b
0012d728 7e2c5440 0012d99c 00000000 00000000 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x2d9
0012d908 7e2c5c25 00000000 ffffffff ffffffff mshtml!CTableLayout::CalculateLayout+0x295
0012da58 7e28b790 0012e854 0012dc0c 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x665
0012db6c 7e2b9ec5 00000000 0012dc0c 00000000 mshtml!CLayout::CalcSize+0x224
vTable pointer has changed to 0x7e2233e1
7e2233e1 e47e2a60 837e28ad d47e4140 dd7e2bdf
7e2233f1 c27e2e84 4d7e4140 1d7e2cf6 817e2b5f
7e223401 b97e2bdc 207e2bce 267e2ef5 147e27ce
7e223411 147e27c9 147e27c9 e17e27c9 a27e28fa
7e223421 267e2919 d97e2b40 707e28a7 687e2cff
7e223431 3f7e28b8 067e2e95 227e28b0 9e7e2d2f
7e223441 537e2b99 61007400 6b006300 6f002000
7e223451 65007600 66007200 6f006c00 00007700
ChildEBP RetAddr Args to Child
0012d668 7e32a1ee 0012d680 0012da2c 0273a4a0 mshtml!CDispNode::SetUserClip+0x9f
0012d728 7e2c5440 0012d99c 00000000 00000000 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x33e
0012d908 7e2c5c25 00000000 ffffffff ffffffff mshtml!CTableLayout::CalculateLayout+0x295
0012da58 7e28b790 0012e854 0012dc0c 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x665
0012db6c 7e2b9ec5 00000000 0012dc0c 00000000 mshtml!CLayout::CalcSize+0x224
vTable pointer has changed to 0x7e2233e1
7e2233e1 e47e2a60 837e28ad d47e4140 dd7e2bdf
7e2233f1 c27e2e84 4d7e4140 1d7e2cf6 817e2b5f
7e223401 b97e2bdc 207e2bce 267e2ef5 147e27ce
7e223411 147e27c9 147e27c9 e17e27c9 a27e28fa
7e223421 267e2919 d97e2b40 707e28a7 687e2cff
7e223431 3f7e28b8 067e2e95 227e28b0 9e7e2d2f
7e223441 537e2b99 61007400 6b006300 6f002000
7e223451 65007600 66007200 6f006c00 00007700
ChildEBP RetAddr Args to Child
0012d668 7e32a1ee 0012d680 0012da2c 0273a4a0 mshtml!CDispNode::SetUserClip+0xb5
0012d728 7e2c5440 0012d99c 00000000 00000000 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x33e
0012d908 7e2c5c25 00000000 ffffffff ffffffff mshtml!CTableLayout::CalculateLayout+0x295
0012da58 7e28b790 0012e854 0012dc0c 00000000 mshtml!CTableLayout::CalcSizeVirtual+0x665
0012db6c 7e2b9ec5 00000000 0012dc0c 00000000 mshtml!CLayout::CalcSize+0x224
日志记录显示,该对象虚表被修改了三次,第一次为初始化虚表,第二次将虚表由0x7e2233e0改为0x7e2233e1,第三次同第二次且同在一个函数里,该函数为mshtml!CDispNode::SetUserClip,部分反汇编如下。
.text:7E40E6CA mov eax, [edi+4] ; 取对象this+4处的一个dword
.text:7E40E6CA ; eax == 0x8c800000
.text:7E40E6CA ; ----------------------------
.text:7E40E6CD and eax, esi ; eax = eax & 0x0f
.text:7E40E6CD ; eax == 0
.text:7E40E6CD ; ----------------------------
.text:7E40E6CF movzx ecx, ds:uchar* CDispNode::_extraSizeTable[eax]
.text:7E40E6CF ; ecx = _extraSizeTable[eax]
.text:7E40E6CF ; ecx == 0
.text:7E40E6CF ; -----------------------------
.text:7E40E6D6 mov eax, edi ; eax = this
.text:7E40E6D6 ; -----------------------------
.text:7E40E6D8 shl ecx, 2 ; ecx = ecx * 4
.text:7E40E6D8 ; ecx == 0
.text:7E40E6D8 ; -----------------------------
.text:7E40E6DB sub eax, ecx ; eax = this - ecx
.text:7E40E6DB ; eax == this
.text:7E40E6DB ; -----------------------------
.text:7E40E6DD [COLOR="red"]or dword ptr [eax], 1[/COLOR] ; vTable 被改写!!!
上述反汇编功能简述为
1,从对象偏移4处取出一个dword(貌似是_flags),得到0x8c800000
2,再取该值的低4位(& 0x0f),得到0。
3,将2得到的低4位作为索引从一个数据表CDispNode::_extraSizeTable中取出一个byte,该数据表长度0x80,内容如下
0:000> db mshtml!CDispNode::_extraSizeTable
7e211c00 00 01 04 05 02 03 06 07-05 06 09 0a 07 08 0b 0c ................
7e211c10 01 02 05 06 03 04 07 08-06 07 0a 0b 08 09 0c 0d ................
7e211c20 1c 1d 20 21 1e 1f 22 23-21 22 25 26 23 24 27 28 .. !.."#!"%$'(
7e211c30 1d 1e 21 22 1f 20 23 24-22 23 26 27 24 25 28 29 ..!". #$"#&'$%()
7e211c40 03 04 07 08 05 06 09 0a-08 09 0c 0d 0a 0b 0e 0f ................
7e211c50 04 05 08 09 06 07 0a 0b-09 0a 0d 0e 0b 0c 0f 10 ................
7e211c60 1f 20 23 24 21 22 25 26-24 25 28 29 26 27 2a 2b . #$!"%&$%()&'*+
7e211c70 20 21 24 25 22 23 26 27-25 26 29 2a 27 28 2b 2c !$%"#&'%&)*'(+,
4,取出的byte依然是0,自乘4(左移两位),依然是0!!!
5,再用this指针减去该值,因为等于什么都没减,所以this依然指向vTable!!!
6,再将vTable和1取或,导致vTable由0x7e2233e0变为0x7e2233e1
经上述分析得知,只要this -> _flags的低4位为0,则vTable将有被改写的风险,至于为什么要通过_flags改写对象紧挨着对象vTable之前的那部分内存,这一部分内存究竟是什么,这要再回头看对象创建过程
int __thiscall CDispNode::operator new(void *this, int a1, int a2, int a3)
{
int v4; // esi@1
LPVOID r_alloc_buffer; // eax@1
int return_this; // edi@1
int v8; // [sp+Ch] [bp-8h]@4
int v9; // [sp+10h] [bp-4h]@4
v4 = 4 * CDispNode::_extraSizeTable[a3];
r_alloc_buffer = _MemAllocClear(this, v4 + a1);
return_this = r_alloc_buffer;
if ( r_alloc_buffer )
{
return_this = (r_alloc_buffer + v4);
*(r_alloc_buffer + v4 + 4) = a3;
if ( a3 & 0x40 )
{
v8 = 0;
v9 = 0;
CDispNode::SetContentOriginNoInval(&v8, -1);
}
}
return return_this;
}
原来在该对象vTable之前,存在一个不定长的buffer,buffer长度来自以参数3为索引,_extraSizeTable数组里的某个值乘以4。再引用llydd前辈的
参考文章1对该处的解释
CTableLayout检查到要显示的结点中含有标题对象时,会检查该标题对象是否有显示结点容器CDispContainer,
当不存在时需为该标题对象创建CDispContainer类型的显示容器对象,同时由于table标签具有style=position:absolute;clip:rect(0),
因而需要对显示的结点进行裁剪,这些信息也会保存到CDispContainer对象中,以供显示之用,
不巧的是CDispContainer是一个变体大小的对象,最小有0x48字节,附加大小是由一张静态表控制:
Sizeof(CDispContainer) = _extraSizeTable[index]*4+0x48,_extraSizeTable[index]*4大小的数据用来存放裁剪信息,
CDispContainer的基本结构为:
CliprectInfo|[COLOR="red"]VT[/COLOR]|CliprectInfo_size|................
尽管关于IE机制的部分完全看不懂,但起码搞明白了在vTable之前确实有一个不定长的buffer,用以保存裁剪信息CliprectInfo,_flags的低四位为一个索引,从附加size表中取出size再乘以4,this减去该值得到裁剪信息buffer的指针,但是这个CDispNode貌似是没有裁剪信息的,因此这里误将vTable当作裁剪信息修改了。
关于利用,窃以为这个漏洞其实是比较垃圾的,只有在从_extraSizeTable表中取出的那个byte为0时,才可以触发,而且只能改虚表指针的最低一位,不仅不能bypass xxx,甚至在ie6不同版本下崩溃时的eip都不一样,只能通过heapspray碰碰运气。
1,CVE-2010-3962漏洞分析 llydd http://bbs.pediy.com/showthread.php?t=125122
2,CVE-2010-3962逆向分析过程 QEvr http://bbs.pediy.com/showthread.php?t=128813
感谢两位前辈分享,其中第二篇文章描述和上述分析稍微有点出入
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课