令牌的故事(CVE-2015-0002)
0x00 前言
這是James Forshaw發表在Project Zero上的文章,主要講了CVE-2015-0002的原理,原文連結http://googleprojectzero.blogspot.com/2015/02/a-tokens-tale_9.html。
我非常喜歡漏洞研究的過程,有時發現漏洞挖掘的難度與利用的難度之間有顯著差異。在Project Zero部落格中包含了很多對看似瑣碎的漏洞的複雜利用過程。你可能會問,為什麼我們會努力證明漏洞是可利用的,我們確實不需要這樣做嗎?希望在這篇部落格的最後,你能更好的理解為什麼我們總是花費很大努力透過開發一個poc來證明一個安全問題。
我們PoC的主要目標是供應商,但開發Poc也有其它的好處。使用供應商系統的客戶可以透過PoC來測試它們的系統是否存在這一漏洞,並確保已經打上了所有的補丁。即使供應商不願意或無法修補漏洞,安全廠商也可以利用它來開發緩解措施和漏洞簽名。而不提供PoC,只有逆向補丁的人才可能瞭解它,他們可能沒有考慮你的最佳利益。
我不希望這個部落格涉及該漏洞(CVE-2015-0002)的太多技術細節。相反,我將重點放在對相對簡單的漏洞的可利用性和PoC開發過程上。該PoC足夠供應商對所描述的漏洞進行一個合理的評估,以減輕工作量。我也會解釋我在PoC開發中採取的各種快捷方式的理由,以及為什麼如此。
0x01 報告漏洞
在封閉或專有系統上進行漏洞研究的最大問題在於修復漏洞的實際報告流程。特別是在複雜的或非顯而易見的漏洞的情況下。如果系統是開源的,可以開發一個補丁並提交,這代表了一個修復的機會。對於閉源系統,就不得不透過報告的流程。為了更好的理解,讓我們想想典型的大型供應商在接受外部的安全漏洞報告時可能需要做的事。
這是漏洞響應處理的一個非常簡單的檢視,但是足以解釋處理原則。對於一個在內部開發他們的大部分軟體的公司,我不能夠影響修補週期,但我可以影響分流週期。越容易對供應商產生影響,分流週期就越短,也就能夠更快的釋出補丁。除了已經在使用此漏洞的人,每個人都獲得了好處。不要忘了,即使之前我不知道這個漏洞並不意味著它不被人瞭解。
在一個理想的漏洞研究的世界(即在其中我只需要做最少量的非科研工作),如果我發現一個bug,我需要做的只是寫了一些關於它的筆記,將其傳送給供應商,他們會了解系統,立即採取行動開發補丁,任務就完成了。當然,這樣是行不通的,讓供應商認識到這是一個安全問題是重要的第一步。這可能是從分流週期轉到補丁週期的主要障礙,特別是他們通常是公司內部獨立的實體。為了得到最好的可能,我可能會做兩件事情:
- 1 在報告中提供足夠詳細的細節,以便供應商瞭解漏洞
- 2 開發出PoC,明確表明安全漏洞的影響
0x02 寫報告
儘管在許多情況下並不夠,但寫報告對供應商修復安全問題是非常關鍵的。你可以想象,如果我寫的東西類似“錯誤在ahcache.sys中,請修復它,lol”,這並不能真正幫助廠商。至少,我需要提供一些背景,例如漏洞影響(不影響)哪些系統,漏洞有什麼影響(盡我所知)和問題存在於系統的什麼地方。
只有報告為什麼並不夠呢?想想大型現代化的軟體產品是如何開發的。它可能是團隊成員分模組獨立開發完成的。根據漏洞程式碼存在的時間,原開發者可能已經轉到其他專案,或者全部離開了公司。即使是身邊可交流的人寫的相對較新的程式碼,也並不意味著他們記得程式碼是如何工作的。任何人在開發任何規模的軟體,都會碰到一個月、一週甚至一天以前他們寫的程式碼,但是不知道它是如何工作的。有一種真實的可能性,花費時間一條一條指令分析軟體的安全研究人員可能比世界上任何人都更瞭解軟體。
你也可以在科學意義上考慮一下報告,也就是脆弱性假說。有些漏洞是可以證明,例如緩衝區溢位,通常可以透過數學方式證明,例如想把10件物品放入只能容納5件的空間裡將不可能實現。但在很多情況下,沒有什麼比開發可利用的證明更好。如果正確完成,可以讓報告者和供應商透過實驗來驗證,這就是概念證明的價值。正確開發概念證明使廠商可以觀察到實驗的效果,將假設變為沒有人可以反駁的理論。
0x03 透過實驗證明可利用性
假說假定漏洞具有真實的安全影響,我們將使用PoC來客觀地證明它。為了做到這一點,我們不止需要向供應商提供證明該漏洞真實性的機理,也需要可以清楚觀察到為什麼這構成一個安全問題。
需要什麼樣的現象取決於漏洞的型別。對於記憶體破壞漏洞,可能只需要證明應用程式在響應某些輸入時崩潰就足夠了。但是並非總是如此,一些記憶體破壞並不提供攻擊者任何有用的控制。因此需要證明可以控制當前執行流,諸如通常理想的是控制EIP暫存器。
對於邏輯漏洞,可能更細緻入微,比如可以寫一個檔案到本不能夠寫的位置或者以提升的許可權執行了計算器程式。沒有一個適合所有情況的方法,但最起碼要表現出可以客觀上觀察到的一些安全影響。
要了解的是我沒有將PoC開發為一個可用的漏洞利用程式(從攻擊者的角度來看),只是足夠證明這是一個安全問題,以保證它能夠被修復。遺憾的是將這兩個者區分開並不容易,有時不展示本地許可權提升或遠端程式碼執行,它的嚴重性就不會受到應有的重視。
0x04 開發概念證明
現在來看看我在開發我發現的ahcache漏洞的PoC時遇到的挑戰。我們不要忘記,花在開發PoC上的時間和漏洞被修復的機率之間要進行權衡。如果我沒有花足夠的時間來開發一個可用的PoC,供應商可能並不會去修復這個漏洞,另一方面,我花的時間越長,這一漏洞的存在就可能對使用者有害。
0x05 漏洞的技術細節
對該漏洞有一點了解將有助於我們後面的討論。這裡(https://code.google.com/p/google-security-research/issues/detail?id=118)你可以看到這一漏洞以及附加的我發給Microsoft的PoC。漏洞存在於ahcache.sys驅動程式中,這是Windows8.1引入的,但本質上是在這驅動程式實現的Windows本地系統呼叫NtApphelpCacheControl中。這個系統呼叫是用來處理糾正在較新版本的Windows上的應用程式行為的應用程式相容資訊本地快取的。你可以在這裡(https://technet.microsoft.com/en-us/windows/jj863248)閱讀更多有關應用程式相容性的資訊。
這個系統呼叫的一些操作是需要許可權的,使驅動對當前呼叫的應用程式進行檢查,以確保他們擁有管理員許可權。這些是在函式AhcVerifyAdminContext中完成的,它看起來像下面的程式碼:
#!c++
BOOLEAN AhcVerifyAdminContext()
{
BOOLEAN CopyOnOpen;
BOOLEAN EffectiveOnly;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
PACCESS_TOKEN token = PsReferenceImpersonationToken(
NtCurrentThread(),
&CopyOnOpen,
&EffectiveOnly,
&ImpersonationLevel);
if (token == NULL) {
token = PsReferencePrimaryToken(NtCurrentProcess());
}
PSID user = GetTokenUser(token);
if(RtlEqualSid(user, LocalSystemSid) || SeTokenIsAdmin(token)) {
return TRUE;
}
return FALSE;
}
此程式碼查詢當前執行緒是否模擬其他使用者。 Windows允許一個執行緒在系統上模擬其他使用者,這樣安全操作就可以正確評估。如果執行緒正在模擬,將返回一個指向訪問令牌的指標。如果從PsReferenceImpersonationToken返回NULL,程式碼則查詢當前程式的訪問令牌。最後,程式碼檢查訪問令牌的使用者是不是本地系統使用者或令牌是不是Administrators組的成員。如果函式返回TRUE,則特權操作被允許繼續進行。
這一切似乎很正確,問題在哪呢?雖然全模擬是僅限於具有令牌模擬特權的使用者才能進行的一種特權操作,普通使用者沒有許可權模擬其他使用者執行非安全相關的功能。核心在模擬啟用時,透過分配一個安全級別給令牌來區分特權和非特權模擬。要理解這個漏洞,只需要關心兩個級別,SecurityImpersonation意味著模擬是特權的和SecurityIdentification是非特權的。
如果令牌被分配為SecurityIdentification,僅能進行查詢令牌資訊的操作,例如查詢令牌的使用者。如果你試圖開啟受保護的資源,如檔案,核心將拒絕訪問。這是潛在的漏洞,如果你看一下程式碼,PsReferenceImpersonationToken返回分配給令牌的安全級別的副本,但是程式碼並未驗證它是否是SecurityImpersonation。這意味著能夠獲取本地系統訪問令牌的普通使用者可以在SecurityIdentification上進行模擬,仍然可以透過檢查,允許對使用者進行查詢。
0x06 證明基本的漏洞利用
要利用該漏洞,需要捕捉本地系統的訪問令牌,模擬它,然後透過適當的引數呼叫系統呼叫。這一定要透過普通使用者的許可權實現,否則就不是一個安全漏洞。該系統呼叫未公開,所以如果我們想走捷徑,可能我們只需要表明我們能夠捕捉令牌,只能做到這樣?
其實並非如此,這個PoC將證明被文件化的可能的事情確實是可能的。即普通使用者可以捕捉令牌並模擬它,作為模擬系統的設計,這不會導致安全問題。我已經知道COM支援模擬,有很多複雜的系統特權服務(例如BITS),我們可以作為普通使用者與其進行互動,為了進行模擬,可以讓它與我們的應用程式進行通訊。這不能證明我們可以到達核心包含漏洞的AhcVerifyAdminContext方法,更別說成功地繞過檢查。
所以,開始了漫長過程的逆向工程,以確定系統呼叫是如何工作的,你需要傳遞什麼引數來使它做一些有用的事。這裡也使用了一些來自其他研究人員的成果(如http://www.alex-ionescu.com/?p=39),但肯定沒有現成可用的東西。該系統呼叫支援許多不同的操作,不是所有的操作所都需要複雜的引數。例如,AppHelpNotifyStart和AppHelpNotifyStop操作可以很容易進行呼叫,他們依賴於AhcVerifyAdminContext函式。現在可以構造PoC,透過觀察系統呼叫的返回程式碼來驗證對檢查的繞過。
#!c++
BOOL IsSecurityVulnerability() {
ImpersonateLocalSystem();
NTSTATUS status = NtApphelpCacheControl(AppHelpNotifyStop, NULL);
return status != STATUS_ACCESS_DENIED;
}
這足以證明漏洞可被利用嗎?歷史已經告訴我不能,例如這個問題(https://code.google.com/p/google-security-research/issues/detail?id=127)包含幾乎完全一樣的操作,即可以透過模擬繞過管理員檢查。在這種情況下,除資訊披露以外,我並沒有足夠的證據證明它是否會導致其他問題。所以它並沒有被修復,即使它確實是一個安全問題。為了證明可利用性,我們需要花更多的時間在PoC上。
0x07 改進的概念證明
為了改進第一個PoC,我需要更好地瞭解系統呼叫在做什麼。應用程式相容性快取用於儲存應用程式相容性資料庫中的查詢資料。這個資料庫包含的規則會告訴應用程式相容性系統什麼可執行檔案需要應用“shims”來實現自定義的行為,如依賴作業系統的版本號來規避不正確的檢查。在程式建立的時候進行查詢,如果找到合適的匹配項,它會被應用到新的程式。新的程式將從資料庫中查詢它需要應用的shim資料。
由於每次建立一個新的程式時都要進行這樣的處理,每次查詢資料庫檔案會帶來顯著的效能開銷。快取有助於降低這種影響,資料庫查詢可以被新增到快取記憶體中。如果可執行檔案稍後被建立,快取查詢可迅速消除耗時的資料庫查詢,同時應用或者不應用一系列的shims。
因此,我們應該能夠快取現有的查詢並將其應用到任意的可執行檔案。所以我花了一些時間獲取系統呼叫的引數的格式,以便新增自己的查詢快取。對於32位Windows 8.1,結構看上去像下面這樣:
#!c++
struct ApphelpCacheControlData {
BYTE unk0[0x98];
DWORD query_flags;
DWORD cache_flags;
HANDLE file_handle;
HANDLE process_handle;
UNICODE_STRING file_name;
UNICODE_STRING package_name;
DWORD buf_len;
LPVOID buffer;
BYTE unkC0[0x2C];
UNICODE_STRING module_name;
BYTE unkF4[0x14];
};
你可以在結構中看到有非常多未知的部分。如果你想將它應用到Windows 7(其結構稍微不同)或64位(其結構大小不同),這會導致問題,但對於我們的目的來說並不重要。我們並不需要寫出能在所有版本的Windows上可用的利用程式碼,我們需要做的只是向供應商證明這是一個安全問題。我們只要告知供應商PoC的限制(他們將注意到限制),這是可以做到的。供應商應該能夠確定該PoC是否可以跨作業系統的版本使用,畢竟這是他們的產品。
所以,現在可以新增任意的快取條目,我們真正新增的是什麼呢?我只能給現有查詢結果新增條目。你可以修改資料庫來做類似執行時程式碼補丁的工作(應用程式相容性系統也可用於修補程式),但這需要管理員許可權。所以,我需要一個現有的shim以便重新利用。
我構建了SDB Explorer工具(https://github.com/evil-e/sdb-explorer)的副本,這樣我可以轉儲現有資料庫查詢中的任何有用的shim。我發現對於32位程式有一個shim會導致程式啟動可執行的regsvr32.exe,並傳遞原始命令列。此工具將載入在命令列上傳遞的一個DLL,執行特定的匯出方法,如果我們能夠控制特權程式的命令列,我們可以把它重定向來提升許可權。
這又限制了PoC只對32位程式有效,但是這很好。最後一步是選擇什麼樣的程式來進行重定向。我花了很多時間研究啟動一個程式的時候並能夠控制它的命令列引數的方法。我已經知道一種方式,即UAC自動提升。自動提升作為一個特性被新增到Windows 7,減少典型使用者看到UAC對話方塊的數量。作業系統定義了允許自動提升的固定的應用程式列表,當UAC為預設設定,且使用者為管理員時,請求提升這些應用程式不會顯示對話方塊。我可以濫用這一點,為現有的自動提升的應用程式新增快取條目(在這種情況下,我選擇了ComputerDefaults.exe),並要求應用程式執行提升。被提升的應用重定向到regsvr32,並傳遞我們完全控制的命令列,regsvr32載入我的DLL,而我們現在得到的程式碼以提升的許可權執行。
另外,PoC沒有提供任何其他東西,不可能透過其他各種機制(如該Metasploit的模組https://github.com/rapid7/metasploit-framework/tree/master/external/source/exploits/bypassuac)來實現,但不總是這樣。透過提供一個可觀察的結果充分展示了這一問題(以管理員身份執行任意程式碼),這樣微軟就能夠重現並解決它。
0x08 有趣的最後一點
由於很容易混淆它是否只能繞過UAC,我決定花一點時間來開發新的PoC,它可以獲得本地系統許可權而不依賴UAC。有時候,我喜歡寫漏洞利用,只是為了證明這是可以做到的。要將原來的PoC轉換為獲得本地系統許可權的PoC,我需要一個不同的應用來重定向。我認為最有可能的目標是註冊的計劃任務(scheduled task),你有時可以將任意引數傳遞給任務處理程式。因此,實現這一任務,有三條限制,一個普通使用者必須能夠啟動它,它必須啟動一個本地系統許可權的程式,程式必須具有由使用者指定的任意的命令列。經過一番搜尋我找到了理想的目標,Windows應用商店維護任務(Windows Store Maintenance Task)。正如我們看到的,它作為本地系統使用者來執行。
透過使用如icacls的工具檢視任務檔案的DACL,我們可以確定普通使用者可以啟動它。注意下面的截圖中的項,NT AUTHORITY \Authenticated Users具有讀取和執行(RX)許可權。
最後,透過檢查XML任務檔案,我們可以檢查普通使用者是否可以來傳遞任意引數給任務。在WSTask中,它使用自定義的COM處理程式(https://msdn.microsoft.com/en-us/library/windows/desktop/aa381370(v=vs.85).aspx),但允許使用者指定兩個命令列引數。這將導致可執行檔案c:\windows\system32\taskhost.exe作為本地系統使用者執行,並且具有任意命令列引數。
這隻需要修改PoC以新增taskhost.exe快取條目,將我們DLL的路徑作為引數來啟動任務。這還是有一定的侷限性,特別是它只能在32位Windows8.1上工作(在64位平臺上沒有32位taskhost.exe可供重定向)。不過我敢肯定,透過一些努力,它也能夠在64位上工作。由於該漏洞已經被修復,所以我提供了新的PoC,它附加在原來的問題之後(https://code.google.com/p/google-security-research/issues/detail?id=118#c159)。
0x09 結論
我希望我已經證明,為了確保漏洞被修復,漏洞研究人員會去做的一些努力。它最終是花費在PoC開發的時間和漏洞被修復之間的一個折衷,尤其是當該漏洞是複雜的或不明顯的時候。
在這種情況下,我覺得我做出了正確的權衡。儘管從我傳送給Microsoft的PoC來看,表面上只是繞過了UAC,結合報告,他們就能夠確定真正的嚴重性並開發補丁。當然,如果他們想推回,並聲稱這不是可利用的,我會開發一個更強大的PoC。透過對嚴重程度的進一步論證,我也開發了一個可用的漏洞利用,可以透過普通使用者帳號獲得本地系統許可權。
披露PoC對使用者或安全公司開發公開漏洞的緩解技術是有價值的。如果沒有PoC,驗證安全問題已被修補或緩解是相當困難的。它還有助於告知研究人員和開發人員,在開發一些安全敏感的應用程式時,什麼型別的問題需要注意。漏洞挖掘不是Project Zero幫助軟體提高安全性的唯一方法,教育也同樣重要。
Project Zero的使命是解決軟體漏洞,開發概念驗證幫助軟體廠商或者開源專案採取明智行動修復漏洞也是我們職責的重要組成部分。
相關文章
- JWT 令牌2020-05-18JWT
- 安全令牌JWT2018-07-11JWT
- Metasploit之令牌竊取2020-09-29
- Hadoop的故事2022-01-18Hadoop
- 我的故事2018-10-27
- “安”的故事2019-03-07
- HTTPS 的故事2018-06-01HTTP
- 二.Go微服務--令牌桶2021-09-01Go微服務
- go的令牌桶實現庫 go-rate2021-04-06Go
- jwc令牌報錯生成失敗2024-07-25
- 什麼是Cookie、令牌與JWT?2024-06-04CookieJWT
- 流量管制-令牌桶與漏桶2022-06-15
- 淺談C#更改令牌ChangeToken2021-09-02C#
- 淺談C#取消令牌CancellationTokenSource2021-08-16C#
- 《GTA》的起源故事2021-05-25
- Linux 核心的故事2020-10-28Linux
- 老爸講的故事2020-04-06
- 故事的開始2019-03-27
- Hadoop - CVE-2021-36151:Hadoop令牌的安全漏洞2022-02-05Hadoop
- 漏桶、令牌桶限流的Go語言實現2020-09-14Go
- 令牌桶、漏斗、冷啟動限流在sentinel的應用2021-01-30
- Go 分散式令牌桶限流 + 兜底策略2022-01-13Go分散式
- 基於多 goroutine 實現令牌桶2019-02-16Go
- DevUI開源的故事2021-11-11devUI
- 母牛的故事 hd 20182020-04-06
- 貓將社的故事2021-03-30
- 我的運維故事2021-03-06運維
- 【Azure Developer】如何驗證 Azure AD的JWT Token (JSON Web 令牌)?2021-10-20DeveloperJWTJSONWeb
- 【5min+】 一個令牌走天下!.Net Core中的ChangeToken2020-03-18
- 邊緣認證和與令牌無關的身份傳播2021-02-17
- Gitlab怎麼使用訪問令牌訪問2024-10-23Gitlab
- 三.Go微服務--令牌桶實現原理2021-09-03Go微服務
- 令牌Token和會話Session原理與攻略2019-05-15會話Session
- 利用CSS注入(無iFrames)竊取CSRF令牌2018-06-21CSS
- 《Arise:一個平凡故事》Polygon:一個悲喜交加的平凡故事2019-12-06Go
- 【位元熊故事匯】2月MVP英雄故事|有愛的開源2022-03-08MVP
- EssenceMediacom:CMO的成長故事2024-11-28
- 關於詩歌的故事2024-09-06