在補丁上戳個洞——利用已經被修復的漏洞實現IE沙箱逃逸

wyzsk發表於2020-08-19
作者: xlab · 2015/08/31 11:50

作者: [email protected]

english version:https://xuanwulab.github.io/2015/08/27/Poking-a-Hole-in-the-Patch/

0x00 The Problem


James Forshaw在2014年11月曾向微軟報告過一個Windows Audio Service的本地許可權提升漏洞。

Windows Audio Service負責管理系統中所有程式的音訊會話。這個服務會把會話引數儲存到HKCU\Software\Microsoft\Internet Explorer\LowRegistry\Audio\PolicyConfig

為了使低許可權程式也可修改音訊會話的引數,服務在儲存時會遞迴設定所有子鍵的ACL為Low IL可控。

若在這個登錄檔鍵下設定一個符號連結指向高許可權登錄檔鍵,就可能導致高許可權登錄檔鍵變為Low IL可控。

0x01 The Patch


微軟釋出了安全公告MS14-071,併發布了KB3005607補丁修復這個漏洞。 這個補丁增加了兩個函式,SafeRegCreateKeyEx和DetectRegistryLink。

DetectRegistryLink大致的程式碼邏輯如下:

#!c++
int DetectRegistryLink(const HKEY key_handle, const wchar_t sub_key_path[], HKEY * out_handle)
{
    int detect_result = 0;
    HKEY sub_key_handle;
    LSTATUS status = RegOpenKeyExW(key_handle,
                                   sub_key_path,
                                   REG_OPTION_OPEN_LINK,
                                   KEY_ALL_ACCESS,
                                   &sub_key_handle);

    if (status != ERROR_SUCCESS) {
        if (status == ERROR_FILE_NOT_FOUND) {
            detect_result = 3;
        } else if (status == ERROR_ACCESS_DENIED) {
            detect_result = 4;
        } else {
            detect_result = 5;
        }
    } else {
        DWORD key_type;
        BYTE data[MAX_PATH * 2];
        DWORD data_size = sizeof(data);

        status = RegQueryValueExW(sub_key_handle, 
                                  kSymbolicLinkValueName, 
                                  nullptr,
                                  &key_type, 
                                  data, 
                                  &data_size);

        if (((status == ERROR_SUCCESS) || (status == ERROR_MORE_DATA)) && (key_type == REG_LINK)) {
            detect_result = 1;
        } 
        if ((status == ERROR_FILE_NOT_FOUND) && (detect_result != 1)) {
            HKEY temp_key_handle;
            status = RegOpenKeyExW(key_handle,
                                   sub_key_path,
                                   0,
                                   KEY_READ,
                                   &temp_key_handle);

            RegCloseKey(temp_key_handle);
            detect_result = (status == ERROR_SUCCESS) + 1;
        }

        *out_handle = sub_key_handle;
    }

    return detect_result;
}

DetectRegistryLink對於登錄檔符號連結做了比較嚴格的判斷。這個函式使用REG_OPTION_OPEN_LINK開啟登錄檔鍵之後處理了多種情況,包括重定向到不存在的鍵值等。最終開啟的登錄檔鍵控制程式碼被傳出函式複用。

外層的SafeRegCreateKeyEx在建立登錄檔鍵之前呼叫了這個函式進行檢查,發現有登錄檔符號連結則使用NtDeleteKey進行刪除,最後統一使用RegCreateKeyEx建立一個新的登錄檔鍵。

#!c++
HKEY sub_key_handle;
int detect_result = DetectRegistryLink(key_handle, kSubKeyPath, &sub_key_handle);

if (detect_result == 1) {
    status = NtDeleteKey(sub_key_handle);
    RegCloseKey(sub_key_handle);
    sub_key_handle = nullptr;

    if (!NT_SUCCESS(status)) {
        return ERROR_ACCESS_DENIED;
    }
}

if (detect_result > 3) {
    if (sub_key_handle) {
        RegCloseKey(sub_key_handle);
    }

    return ERROR_ACCESS_DENIED;
}

DWORD create_disposition = 0;

if (sub_key_handle) {
    create_disposition = REG_OPENED_EXISTING_KEY;
} else {
    status = RegCreateKeyExW(key_handle,
                             kSubKeyPath,
                             0,
                             nullptr,
                             0,
                             KEY_ALL_ACCESS,
                             nullptr,
                             &sub_key_handle,
                             &create_disposition);

    if (status != ERROR_SUCCESS) {
        return status;
    }

    if (create_disposition != REG_CREATED_NEW_KEY) {
        RegCloseKey(sub_key_handle);
        return ERROR_ACCESS_DENIED;
    }
}

0x02 The Flaw


邏輯看似很嚴密,然而存在一個比較嚴重的問題。

在使用NtDeleteKey刪除目標登錄檔鍵之後,系統不再允許對它進行操作。雖然已開啟的控制程式碼繼續有效,但任何操作都會返回STATUS_KEY_DELETED,只能關閉控制程式碼。

在控制程式碼關閉後,後續操作就只能使用物件名開啟一個新的控制程式碼。在這種情況下,系統並不保證和之前同名的物件就是同一個物件。

透過精確的時間差攻擊,我們可以搶在RegCreateKeyEx被呼叫之前建立一個符號連結,從而繞過判斷。

0x03 The Exploit


我們仍然以IE 11沙箱為例,說明如何利用此漏洞提升許可權。

為了滿足利用條件,首先需要讓Windows Audio Service做出刪除動作。

我們可以故意在HKCU\Software\Microsoft\Internet Explorer\LowRegistry\Audio\PolicyConfig登錄檔鍵下建立一個符號連結,並觸發Windows Audio Service寫入,這樣就會走入刪除邏輯。

如何精確控制寫入符號連結的時間很重要。我們當然可以開十萬個執行緒迴圈寫入,總有一天會成功。但其實系統已經提供了這樣的觸發機制。

NtNotifyChangeKey可以監視一個登錄檔鍵,當我們指定的登錄檔操作發生時,設定一個事件訊號。

透過在符號連結上設定通知,我們可以在符號連結被Windows Audio Service刪除時立刻觸發,並有機會搶在Windows Audio Service建立新的登錄檔鍵之前建立一個符號連結。

將符號連結指向HKCU\Software\Microsoft\Internet Explorer\Low Rights\ElevationPolicy登錄檔鍵下的一個尚不存在的GUID,就可以滿足REG_CREATED_NEW_KEY的判斷,成功建立目標登錄檔鍵。

之後Windows Audio Service會使用上層登錄檔鍵(PolicyConfig)的安全性設定(Low IL可控)覆蓋目標登錄檔鍵的安全性設定,導致剛建立的ElevationPolicy鍵值可被IE沙箱內程式寫入。

這時寫入任意的AppPath,並將Policy設定為0x3,即可在IE沙箱內以Medium IL啟動任意程式。

0x04 The Trick


Windows Audio Service的登錄檔操作是在RpcImpersonateClient之後進行的。所以在IE沙箱中直接操作雖然可以競爭成功,但登錄檔操作會使用源程式的Low IL令牌,許可權不足。

James Forshaw在原版的PoC中未能解決這個問題,只能在外部手動啟動SndVol.exe觸發。

為了解決這個問題,我們需要觸發一個Medium IL以上的程式使用音訊會話,通常只需要程式發出聲音即可。

IE Elevation Policy中預設設定了一些程式可以在沙箱內用Medium IL啟動,其中就包括記事本(Notepad.exe)。Medium IL程式啟動後我們只有結束程式的許可權,但在啟動程式時我們可以傳遞命令列引數。

記事本在開啟不存在的檔案時會彈出一個系統對話方塊詢問是否需要建立新檔案,伴隨一聲系統預設聲音。這就足夠觸發Windows Audio Service寫入登錄檔鍵了。

透過反覆啟動嘗試,我們就能多次競爭,保證最終成功。

0x05 The Mitigation


微軟最終在2015年8月的補丁中徹底禁止了Low IL程式建立登錄檔符號連結。在設定登錄檔符號連結時,核心透過RtlIsSandboxedToken函式判斷當前程式令牌是Low IL或AppContainer則直接返回拒絕訪問。這導致任何基於登錄檔符號連結的攻擊在Low IL都無法使用了,因此在IE沙箱內直接利用這個漏洞的可能性被徹底封堵了。

0x06 References


Issue 99: IE11 AudioSrv RegistryKey EPM Privilege Escalation – James Forshaw https://code.google.com/p/google-security-research/issues/detail?id=99

Windows 音訊服務中的漏洞可能允許特權提升 (3005607) https://technet.microsoft.com/library/security/MS14-071

Windows 10 Symbolic Link Mitigations – James Forshaw https://googleprojectzero.blogspot.com/2015/08/windows-10hh-symbolic-link-mitigations.html

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章