一個小小指針,竟把Linux內(nèi)核攻陷了!
怎樣攻進(jìn)操作系統(tǒng)內(nèi)核?
這是一個很有意思也很硬核的問題。
黑客通過應(yīng)用程序的漏洞(如Java、PHP、Apache、IE、Chrome、Adobe、office等)獲得執(zhí)行代碼能力后,由于操作系統(tǒng)安全方面的設(shè)定,很多情況下都是在沙盒或者低權(quán)限進(jìn)程中運(yùn)行,許多操作都無法進(jìn)行。要想做更多高權(quán)限的事情,黑客通常會使用工具來提權(quán)。
x86
arm
操作系統(tǒng)的安全防線是建立在內(nèi)核至高權(quán)限掌控的基礎(chǔ)之上,無論是殺毒軟件,沙箱,還是防火墻都運(yùn)行在內(nèi)核態(tài)。要突破安全包圍,必須獲得內(nèi)核級權(quán)限的執(zhí)行能力,才能和這些安全防護(hù)正面PK。
我們常常聽到的Android系統(tǒng)ROOT和iOS系統(tǒng)的越獄就是內(nèi)核攻擊的典型應(yīng)用。
獲得內(nèi)核權(quán)限以后,攻擊者可安裝rootkit級木馬病毒,實(shí)現(xiàn)文件隱藏、進(jìn)程隱藏、通信隱藏等高級木馬功能,對系統(tǒng)危害極為嚴(yán)重。
內(nèi)核0day漏洞,在APT攻擊中是核武器級別的存在,地下網(wǎng)絡(luò)安全交易市場價值巨大。
進(jìn)入內(nèi)核的四種方式
實(shí)際上,我們的程序每時每刻都在往返于用戶空間和內(nèi)核空間,只不過這些進(jìn)出的大門都被操作系統(tǒng)提前安排好了,進(jìn)入內(nèi)核后該去哪里執(zhí)行什么代碼,是操作系統(tǒng)說了算,由不得我們自己的程序做主。
從用戶態(tài)空間進(jìn)入內(nèi)核,有四種方式:
中斷:
中斷分為兩種:硬中斷和軟中斷
硬中斷:硬件設(shè)備向CPU發(fā)起的中斷信號
軟中斷:CPU執(zhí)行int指令觸發(fā),早期的操作系統(tǒng)中實(shí)現(xiàn)系統(tǒng)調(diào)用就是通過這種方式,如Windows上的 int 2e,Linux上的 int 80
不管是硬中斷還是軟中斷,CPU遇到以后都會保留當(dāng)前執(zhí)行的現(xiàn)場上下文,進(jìn)入內(nèi)核去執(zhí)行中斷處理函數(shù)。這些函數(shù)記錄在中斷描述符表IDT中,由操作系統(tǒng)初始化系統(tǒng)的時候預(yù)先安排。
異常:
異常是CPU在執(zhí)行指令的過程中出現(xiàn)的問題,如除法指令的除數(shù)為0,訪問的內(nèi)存地址無效等等。
異常和中斷處理邏輯類似,也是通過記錄在IDT中的異常處理函數(shù)來執(zhí)行,同樣由操作系統(tǒng)初始化系統(tǒng)的時候預(yù)先安排。
系統(tǒng)調(diào)用:
系統(tǒng)調(diào)用大家應(yīng)該就很熟悉了,我們要實(shí)現(xiàn)文件系統(tǒng)訪問、網(wǎng)絡(luò)I/O、進(jìn)程線程使用、內(nèi)存分配釋放等等行為,都需要借助操作系統(tǒng)提供的編程接口來實(shí)現(xiàn),這些接口叫做:系統(tǒng)調(diào)用。
前面提到,早期的x86架構(gòu)下的CPU,沒有專門的系統(tǒng)調(diào)用機(jī)制,操作系統(tǒng)們都使用軟中斷的形式來進(jìn)入內(nèi)核完成系統(tǒng)調(diào)用。
后來,因?yàn)橄到y(tǒng)調(diào)用是一個很高頻的需求,軟中斷的方式效率有些低下,CPU加入了專門的系統(tǒng)調(diào)用機(jī)制,這包括一些專用的寄存器和一些專用的系統(tǒng)調(diào)用指令。如sysenter(x86)、syscall(x64)、swi(arm)。
通過系統(tǒng)調(diào)用進(jìn)入內(nèi)核后,該轉(zhuǎn)向哪里執(zhí)行也是操作系統(tǒng)提前安排好了,由不得應(yīng)用程序做主。
開發(fā)驅(qū)動程序:
最后一種進(jìn)入內(nèi)核的方式就是開發(fā)驅(qū)動程序了,但加載驅(qū)動本身就需要極高的權(quán)限,所以這一點(diǎn)就不詳述了。
以上就是通過正規(guī)途徑,讓我們的程序進(jìn)入內(nèi)核態(tài)運(yùn)行的方式,可見,一旦進(jìn)入內(nèi)核態(tài),執(zhí)行流就進(jìn)入了操作系統(tǒng)提前設(shè)置好的代碼,攻擊者沒辦法胡來。
有正規(guī)途徑,當(dāng)然就有不正規(guī)途徑,也就是通過形形色色的漏洞攻擊系統(tǒng)內(nèi)核,從而使我們的程序進(jìn)入內(nèi)核態(tài)執(zhí)行,這也是這篇文章的重點(diǎn)。
下面列舉一些常見的攻擊手法。
零地址攻擊
學(xué)過C語言的朋友都知道,零地址,也就是NULL,在C語言中代表著空指針。
一些沒有經(jīng)驗(yàn)的程序員在寫一些接口函數(shù)時,往往容易忘記檢查指針參數(shù)是否是NULL,而導(dǎo)致程序的崩潰異常。
以32位操作系統(tǒng)為例,進(jìn)程的地址空間是:
- 0x00000000~0xFFFFFFFF,
在x86架構(gòu)上,內(nèi)存一般以4KB頁面單元進(jìn)行管理。
你有沒有想過,如果進(jìn)程的地址空間中,以零地址(也就是NULL)開始的第一個4KB頁面如果被分配了,會出現(xiàn)什么事情?
假設(shè)在內(nèi)核中,有一段代碼忘記對空指針的檢查,就通過這個指針來調(diào)用函數(shù)。而攻擊者提前將零地址頁面分配準(zhǔn)備好數(shù)據(jù),就像這樣:
這會發(fā)生什么后果?
后果就是,攻擊者的代碼將會在內(nèi)核態(tài)下執(zhí)行!
然而假設(shè)不只是假設(shè),它曾真實(shí)發(fā)生過,就算強(qiáng)如微軟的程序大佬,也會有忘記檢查空指針的時候。
典型漏洞案例:CVE-2014-4113 Windows
釋放后使用:UAF
除了空指針,懸在C/C++程序員頭頂?shù)倪€有一把利刃,這就是懸空指針。
懸空指針的意思是忘記對已經(jīng)釋放的內(nèi)存/對象指針即時置空,而在后面又去使用這個指針,但此時對應(yīng)的內(nèi)存已被回收,引發(fā)不可預(yù)期的后果。
哎,這個指針可真是害人不淺啊!
你有沒有想過,假如在對象釋放后忘記對指針及時置空,后面又繼續(xù)使用這個指針,就在這兩個動作發(fā)生之間的那一段時間里,不懷好意的人去把原來釋放的那塊內(nèi)存空間給“占領(lǐng)”了,布置好惡意的數(shù)據(jù)代碼,會發(fā)生什么后果?
這就是大名鼎鼎的釋放后使用UAF攻擊!
UAF意思是Use After Free。
來看一個簡單的例子:
兩個對象,一個真,一個假,它倆對象占據(jù)的內(nèi)存空間一樣大。
下面這段代碼,在原始對象釋放后,忘記對obj指針置空,隨后分配一個FakeObject,由于堆分配算法的原因,這倆對象一樣大小,很大概率新的對象就會分配到剛剛釋放的那片內(nèi)存上去。
此時再通過原來已經(jīng)懸空的指針來調(diào)用函數(shù),實(shí)際上調(diào)用到了新的對象的函數(shù),劫持了執(zhí)行流程。
這只是一個簡單的示例,真實(shí)環(huán)境中比這要復(fù)雜得多,但原理是一致的。
而這種事情一旦發(fā)生在系統(tǒng)內(nèi)核,那后果就嚴(yán)重了,應(yīng)用程序可以劫持內(nèi)核空間的執(zhí)行流程,執(zhí)行自己的代碼。
典型漏洞案例:CVE-2016-0728 Linux
整數(shù)溢出 + 數(shù)組越界
在操作系統(tǒng)中,有很多函數(shù)地址以表格的形式存儲了起來,如:
- 系統(tǒng)調(diào)用表:SSDT/sys_call_table
- 中斷描述符表:IDT
假如有辦法能修改這些表格中的函數(shù)地址,改寫成攻擊者的代碼地址,不就能有辦法讓我們的代碼在內(nèi)核模式下運(yùn)行了嗎?
道理是這么個道理,但這些表格本身就位于內(nèi)核空間,普通應(yīng)用程序別說去改寫了,連讀取都費(fèi)勁。
那真的沒辦法了嗎?
還是有的!
假如內(nèi)核中某段代碼在向某個數(shù)組中某個元素寫入數(shù)據(jù),又恰巧忘記了檢查數(shù)組的下標(biāo)是不是越界,再恰好這個下標(biāo)可以通過應(yīng)用程序來控制,那豈不是可以越界寫?一不小心寫到了前面那些函數(shù)表格里去了咋辦?
你可能已經(jīng)意識到了,這不是假設(shè),是真實(shí)案例!
典型漏洞案例:CVE-2013-2094 Linux
這是一個Linux內(nèi)核任意地址寫入漏洞,通過精準(zhǔn)控制系統(tǒng)調(diào)用的參數(shù),實(shí)現(xiàn)改寫IDT中的函數(shù)地址為惡意代碼地址,實(shí)現(xiàn)在內(nèi)核態(tài)執(zhí)行惡意代碼!
下面是一些內(nèi)核攻擊的真實(shí)案例:
安全防御
魔高一尺,道高一丈,形形色色的攻擊也推進(jìn)了操作系統(tǒng)和CPU的安全能力建設(shè):
- Intel? 2011年在第三代Core處理器禁止內(nèi)核狀態(tài)下去執(zhí)行用戶地址空間指令 SMEP(supervisor mode execute prevention),設(shè)置CR4寄存器的bit20位為1開啟
- ARM從armv7開始加入PXN技術(shù),原理同SMEP
- Windows 8.1禁止使用零頁地址內(nèi)存
- Linux 2.6.26開始使用vm.mmap_min_addr限制地址空間最小值,防止使用零頁內(nèi)存
- ······
空指針、懸空指針、數(shù)組越界、整數(shù)溢出···這些一個個看起來不起眼的編程問題,如果發(fā)生在操作系統(tǒng)內(nèi)核之中,那造成的后果便是災(zāi)難性的!可見,養(yǎng)成一個好的編程習(xí)慣有多重要!
連開發(fā)操作系統(tǒng)的大神程序員們都會犯錯誤,何況我們呢?
本文轉(zhuǎn)載自微信公眾號「編程技術(shù)宇宙」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系編程技術(shù)宇宙公眾號。