Exploit-DB #50691
Description: Mozilla Firefox 67 - Array.pop JIT Type Confusion
Type: local
Platform: windows
Date: 2022-02-02
Author: Forrest Orr

Exploit:
# Exploit Title: Mozilla Firefox 67 - Array.pop JIT Type Confusion
# Date: 2021-12-07
# Type: RCE
# Platform: Windows
# Exploit Author: deadlock (Forrest Orr)
# Author Homepage: https://forrest-orr.net
# Vendor Homepage: https://www.mozilla.org/en-US/
# Software Link: https://ftp.mozilla.org/pub/firefox/releases/65.0.1/win64/en-US/
# Version: Firefox 67.0.2 64-bit and earlier
# Tested on: Windows 10 x64
# CVE: CVE-2019-11707
# Bypasses: DEP, High Entropy ASLR, CFG
# Full Hydseven exploit chain with sandbox escape (CVE-2019-11708): https://github.com/forrest-orr/Exploits/tree/main/Chains/Hydseven

<html>
<head>
</head>
<body>
<script>
/*
  _______ ___ ___ _______        _______ _______ _____ _______        _____ _____ _______ _______ _______ 
 |   _   |   Y   |   _   |______|       |   _   | _   |   _   |______| _   | _   |   _   |   _   |   _   |
 |.  1___|.  |   |.  1___|______|___|   |.  |   |.|   |   |   |______|.|   |.|   |___|   |.  |   |___|   |
 |.  |___|.  |   |.  __)_        /  ___/|.  |   `-|.  |\___   |      `-|.  `-|.  |  /   /|.  |   |  /   / 
 |:  1   |:  1   |:  1   |      |:  1  \|:  1   | |:  |:  1   |        |:  | |:  | |   | |:  1   | |   |  
 |::.. . |\:.. ./|::.. . |      |::.. . |::.. . | |::.|::.. . |        |::.| |::.| |   | |::.. . | |   |  
 `-------' `---' `-------'      `-------`-------' `---`-------'        `---' `---' `---' `-------' `---'  

Overview
                                 
This is a Windows variation of CVE-2019-11707, an exploit targetting a type
confusion bug in the Array.pop method during inlining/IonMonkey JIT compilation
of affected code in versions of Firefox up to 67.0.2.

Fundamentally this bug allows an attacker to trick IonMonkey into JIT'ing a
function popping and accessing an element of a specially crafted malicious
array without generating any speculative guards on the element type. In other
words, we can reliably produce an ASM routine for a JS function which is only
designed to handle array element access for a specific object type, while
allowing us to effectively modify the type of the element being accessed. Thus
a class object may be accessed as a float, a float as an integer, and so on.
The end result is a classic type confusion on the ASM layer which is leveraged
into an OOB array access, providing the basis for construction of R/W/AddressOf
primitives.

More specifically this bug allows for the creation of specially crafted malicious 
arrays with a specific element type set. By creating a function which loops
through this malicious array and calls Array.pop on its elements, IonMonkey
can be made to JIT an ASM routine specifically optimized to only handle this
one specific type of array element. The bug comes into affect in the unique
edge case of an object prototype: when Array.pop attempts to access an element
at an index which does not exist (such as in a sparse array) it will then make
a secondary, fall-back attempt to access this element index on the prototype
of its associated array. This would not be an issue if IonMonkey tracked
modifications to the type sets of prototype elements but it does not.

...

bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
    trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
    return InliningStatus_NotInlined;
    }

...

This was the vulnerable piece of code in IonMonkey which enabled the bug. It
can be plainly seen that they did attempt to check types of indexed elements
on array prototypes but did so incorrectly: every array will by default have a
special ArrayPrototype object associated with it. However, we do not need to
leave this default layout intact. We can set a custom prototype on our
malicious array (this custom prototype itself being an array) and trick the
engine into checking the ArrayPrototype of our custom prototype for indexed
elements instead of the custom prototype which contains the malicious untracked
elements. Practically speaking:

var SparseTrapdoorArray = [BugArrayUint32, BugArrayUint32];

This will produce:

SparseTrapdoorArray -> ArrayPrototype

Now if a new array is created and set as the custom prototype of
SparseTrapdoorArray:

var CustomPrototype = [new Uint8Array(BugArrayBuf)];
SparseTrapdoorArray.__proto__ = CustomPrototype;

This will produce:

SparseTrapdoorArray -> CustomPrototype -> ArrayPrototype

Thus an element access on a non-existent element of SparseTrapdoorArray will
access this same index on CustomPrototype instead, and it will be the
ArrayPrototype of CustomPrototype which is checked by IonMonkey during
inlining, not the actual prototype of the SparseTrapdoorArray array ie. the
CustomPrototype. If SparseTrapdoorArray[0] were to not exist and be accessed,
it would result in an access to the Uint8Array element at CustomPrototype[0]
despite the JIT'd function being optimized for access to Uint32Array at 
SparseTrapdoorArray[0].

~

Design

I created the exploit primitives for CVE-2019-11707 in much the same way as I
did CVE-2019-17026: the heap is groomed so that 3 objects are lined up
in memory. In this case they are ArrayBuffers.

[ArrayBuffer 1][ArrayBuffer 2][ArrayBuffer 3]

We use the bug to overflow array 1 and corrupt the ArrayBuffer of array 2,
artificially augmenting its length to encompass the NativeObject of array 3.
From this point onward, array 2 is used to corrupt the slots pointer within the
NativeObject of array 3 to do arbitrary reads, writes and addrof. 

Once these primitives are obtained, a JIT spray is used to plant an egg hunter
shellcode in +RX memory within the firefox.exe content process being hijacked.
The ASM source for my egg hunter can be found here:
https://github.com/forrest-orr/Exploits/blob/main/Payloads/Source/DoubleStar/Stage1_EggHunter/Egghunter64.asm

The role of this egg hunter is to search o