一些调试的东西
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
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.
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.