作者:啟明星辰ADLab
公眾號:https://mp.weixin.qq.com/s/eowDUm2xmpXYK5w1mdD8JA

漏洞概述

2018年10月,啟明星辰ADLab發現瀏覽器WebAssembly模塊存在高危漏洞,并第一時間通報蘋果和微軟官方進行修復。該漏洞位于對應瀏覽器JavaScript引擎(JavaScriptCore/ChakraCore)與WebAssembly模塊的接口,可同時影響Edge、Safari瀏覽器。

2019年3月25日,蘋果發布了針對該漏洞的安全補丁(CVE-2019-6201);微軟的對應漏洞補丁(CVE-2019-0607)已于2019年2月12日發布。提醒廣大用戶盡快將瀏覽器升級到最新版本。

漏洞影響范圍

  • Microsoft Windows 10操作系統的Edge瀏覽器
  • Apple iOS/macOS操作系統的Safari瀏覽器
  • 其他平臺上基于WebKit的組件和產品

漏洞簡析

攻擊者可通過精心構造的html網頁,使用戶在使用瀏覽器訪問網頁時觸發漏洞。該漏洞在瀏覽器漏洞利用中可以直接作為fakeobj原語。通常addrof與fakeobj原語結合可以直接獲得任意代碼執行的能力,在一些特殊情況下,單獨使用fakeobj原語也可以完成漏洞利用。

該漏洞的簡要分析如下(以Safari/WebKit CVE-2019-6201為例):

WebAssemblyModuleRecord::link負責解析WebAssembly模塊中的各個結構,在解析導出表時,有:

    case Wasm::ExternalKind::Global: {
        // Assert: the global is immutable by MVP validation constraint.
        const Wasm::Global& global = moduleInformation.globals[exp.kindIndex];
        ASSERT(global.mutability == Wasm::Global::Immutable);
        // Return ToJSValue(v).
        switch (global.type) {
        case Wasm::I32:
            exportedValue = JSValue(m_instance->instance().loadI32Global(exp.kindIndex));
            break;
        case Wasm::I64:
            throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "exported global cannot be an i64"_s));
            return;
        case Wasm::F32:
            exportedValue = jsValue(m_instance->instance().loadF32Global(exp.kindIndex));
            break;
        case Wasm::F64:
            exportedValue = jsValue(m_instance->instance().loadF64Global(exp.kindIndex));
            break;
        default:
            RELEASE_ASSERT_NOT_REACHED();
    }

在加載導出的全局變量時,有Wasm::I32Wasm::I64Wasm::F32Wasm::F64四種類型,是WebAssembly標準中指定的數據類型(descriptor),分別表示32位、64位的整數和浮點數,在.wasm文件中用一個字節確定;隨后根據變量類型的長度從.wasm文件中繼續取出具體數據(value),封裝成JSValue供JavaScript上下文使用。

以“case Wasm::F64為例”,debug版的代碼會檢查外來數據是否是一個符合IEEE754標準的雙精度浮點數:

#define DoubleEncodeOffset 0x1000000000000ll

ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, double d)
{
    ASSERT(!isImpureNaN(d));
    u.asInt64 = reinterpretDoubleToInt64(d) + DoubleEncodeOffset;
}
inline bool isImpureNaN(double value)
{
    // Tests if the double value would break JSVALUE64 encoding, which is the most
    // aggressive kind of encoding that we currently use.
    return bitwise_cast<uint64_t>(value) >= 0xfffe000000000000llu;
}

Release版本會在編譯過程將isImpureNaN這一檢查去掉,此時外來數據如果是一個NaN(Not a Number),例如0xffff000000888888,在通過加法(+DoubleEncodeOffset)封裝成JSValue時會發生溢出,變成0x888888。由于Safari的boxing規則,這樣的一個JSValue會被當作指針,因而發生類型混淆漏洞。

漏洞修補則順其自然地把去掉的檢查補回來:

case Wasm::F32:
-   exportedValue = jsValue(m_instance->instance().loadF32Global(exp.kindIndex));
+   exportedValue = jsNumber(purifyNaN(m_instance->instance().loadF32Global(exp.kindIndex)));
    break;
case Wasm::F64:
-   exportedValue = jsValue(m_instance->instance().loadF64Global(exp.kindIndex));
+   exportedValue = jsNumber(purifyNaN(m_instance->instance().loadF64Global(exp.kindIndex)));
    break;

Edge瀏覽器的漏洞和補丁也非常相似:

Var WebAssemblyInstance::CreateExportObject(WebAssemblyModule * wasmModule, ScriptContext* scriptContext, WebAssemblyEnvironment* env)
{
    Js::Var exportsNamespace = scriptContext->GetLibrary()->CreateObject(scriptContext->GetLibrary()->GetNull());
    for (uint32 iExport = 0; iExport < wasmModule->GetExportCount(); ++iExport)
    {
        Wasm::WasmExport* wasmExport = wasmModule->GetExport(iExport);
        Assert(wasmExport);
        if (wasmExport)
        {
            PropertyRecord const * propertyRecord = nullptr;
            scriptContext->GetOrAddPropertyRecord(wasmExport->name, wasmExport->nameLength, &propertyRecord);
            Var obj = scriptContext->GetLibrary()->GetUndefined();
            switch (wasmExport->kind)
            {
            ...
            case Wasm::ExternalKinds::Global:
                Wasm::WasmGlobal* global = wasmModule->GetGlobal(wasmExport->index);
                if (global->IsMutable())
                {
                    JavascriptError::ThrowTypeError(wasmModule->GetScriptContext(), WASMERR_MutableGlobal);
                }
                Wasm::WasmConstLitNode cnst = env->GetGlobalValue(global);
                switch (global->GetType())
                {
                case Wasm::WasmTypes::I32:
                    obj = JavascriptNumber::ToVar(cnst.i32, scriptContext);
                    break;
                case Wasm::WasmTypes::I64:
                    JavascriptError::ThrowTypeErrorVar(wasmModule->GetScriptContext(), WASMERR_InvalidTypeConversion, _u("i64"), _u("Var"));
                case Wasm::WasmTypes::F32:
-                   obj = JavascriptNumber::New(cnst.f32, scriptContext);
+                   obj = JavascriptNumber::NewWithCheck(cnst.f32, scriptContext);
                    break;
                case Wasm::WasmTypes::F64:
-                   obj = JavascriptNumber::New(cnst.f64, scriptContext);
+                   obj = JavascriptNumber::NewWithCheck(cnst.f64, scriptContext);
                    break;
                ...
                }
            }
            JavascriptOperators::OP_SetProperty(exportsNamespace, propertyRecord->GetPropertyId(), obj, scriptContext);
        }
    }
    DynamicObject::FromVar(exportsNamespace)->PreventExtensions();
    return exportsNamespace;
}

可以看到,在WebAssembly標準的實現中微軟、蘋果犯了類似的錯誤,導致漏洞的面貌也極其相似,漏洞原理也并不復雜。該漏洞是在WebAssembly功能實現時直接引入的,在Edge、Safari中已潛伏了2年。

另一方面,由于JavaScript引擎也無法良好地實現i64類型的WebAssembly變量,因此無論是Safari/WebKit還是Edge都拒絕對該類型及進行處理。MDN也在WebAssembly導出函數章節提到:“如果你嘗試調用一個接受或返回一個i64類型導出的wasm函數,目前它會拋出一個錯誤,因為JavaScript沒有精確的方式來標識一個i64。不過,這在將來可能會改變——在將來的標準中,將考慮新的i64類型。屆時,wasm可以使用它”。

這給我們的啟示:

  • 新技術、新標準會帶來新的攻擊面,標準的實現過程可能會伴隨安全問題。
  • 不同模塊耦合時可能會打破某模塊內部的假設,需要謹慎對待。

根據該漏洞的特點,啟明星辰ADLab已連續發現了若干漏洞和代碼問題,并已通報廠商進行修復。

漏洞時間軸

  • 2018年10月30日,啟明星辰ADLab向蘋果提交漏洞;
  • 2018年11月6日,啟明星辰ADLab向微軟提交漏洞;
  • 2018年11月27日,蘋果在WebKit代碼庫中修復漏洞;
  • 2019年1月24日,微軟在ChakraCore代碼庫中修復漏洞;
  • 2019年2月12日,微軟為Edge瀏覽器推送安全性更新,并披露CVE編號;
  • 2019年3月25日,蘋果為Safari瀏覽器等產品推送安全性更新,并披露CVE編號。

安全建議

安裝廠商推送的安全性更新,更新至最新版本。

為了方便社區貢獻代碼,Edge、Safari在內的常見瀏覽器產品往往將核心引擎組件開源,而開源代碼倉庫中的每次補丁提交均包含部分漏洞信息。因此在廠商正式披露漏洞并為產品推送補丁之前,黑客有一個構造漏洞POC的攻擊時間窗。為了縮小這一時間窗,終端用戶應及時安裝廠商提供的安全性更新。

參考鏈接


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/870/