英特爾正在推廣一項(xiàng)很靈活的技術(shù)。該技術(shù)可以在處理器層面上阻擋惡意軟件感染,其相關(guān)細(xì)節(jié)已經(jīng)在上周四發(fā)表。實(shí)際上是這樣:英特爾稱之為控制流強(qiáng)制技術(shù) (Control-flow Enforcement Technology, CET) 嘗試阻撓會(huì)使用面向返回編程 (Return-orientated programming, ROP) 和面向跳轉(zhuǎn)編程 (Jump-orientated programming, JOP) 的漏洞利用代碼。
CET通過引入一個(gè)影子堆棧來工作。影子堆棧僅包括返回地址,它存儲(chǔ)在系統(tǒng)RAM中,并受到CPU的內(nèi)存管理模塊保護(hù)。當(dāng)子程序受到調(diào)用時(shí),返回地址隱藏在線程的棧當(dāng)中,通常而言,它也會(huì)在影子堆棧中。當(dāng)處理器返回一個(gè)指令時(shí),會(huì)確保在線程堆棧上的返回地址與影子堆棧中的地址匹配。
如果并不匹配,系統(tǒng)將拋出一個(gè)異常,讓操作系統(tǒng)能夠捕捉并停止執(zhí)行。因此,如果漏洞利用代碼開始篡改堆棧,將惡意指令串在一起,在系統(tǒng)上安裝惡意軟件或用其它方式攻擊系統(tǒng),這類篡改將會(huì)被檢測(cè)到,并且可以在造成任何傷害之前制止它們。
影子堆棧必須在頁(yè)表上具有新的影子堆棧集的內(nèi)存上存儲(chǔ)。如果軟件嘗試通過訪問影子堆棧實(shí)現(xiàn)惡意企圖,比如利用MOV指令,就會(huì)被內(nèi)存管理單元和操作系統(tǒng)提出的一個(gè)頁(yè)面錯(cuò)誤警告所制止。如果軟件在影子堆棧沒有被標(biāo)記為影子堆棧的情況下嘗試使用控制流指令,比如RET,頁(yè)表同樣會(huì)提出一個(gè)錯(cuò)誤警告。
運(yùn)行中線程使用的影子堆棧指針 (Shadow stack pointer, SSP) 存儲(chǔ)在任務(wù)狀態(tài)段中。有各種各樣的控制寄存器能夠?qū)μ貦?quán)ring 0到ring 2(非用戶狀態(tài)ring)和中斷獲取SSPs。如果想要了解這項(xiàng)技術(shù)的細(xì)節(jié),可以參考這篇PDF文檔。
這種技術(shù)阻止的到底是什么?
你經(jīng)常能做到這樣的事情:找到某個(gè)軟件中的內(nèi)存緩沖區(qū),并在數(shù)組中注入更多的數(shù)據(jù),從而讓多出的字節(jié)溢出到其它變量和指針的位置上。最終你將粉碎堆棧上的返回地址,并讓它指向某個(gè)有效的惡意代碼。當(dāng)運(yùn)行函數(shù)返回時(shí),處理器不會(huì)跳回到軟件的某個(gè)合法部分,而是跳回到你定義的重寫堆棧部分,也即你的惡意載荷。
只要通過互聯(lián)網(wǎng)進(jìn)行傳播,你就可以在其他人的系統(tǒng)上實(shí)現(xiàn)執(zhí)行任意代碼。他們的盒子現(xiàn)在變成了你的盒子。
然后,操作系統(tǒng)和處理器開始實(shí)施機(jī)制防止上述情況。那個(gè)堆棧存儲(chǔ)在內(nèi)存頁(yè)表上的方式是數(shù)據(jù),而不是可執(zhí)行代碼。因此,很容易在發(fā)生破壞之前解決這類攻擊:如果處理器開始嘗試執(zhí)行在非可執(zhí)行區(qū)域存儲(chǔ)的代碼,也即數(shù)據(jù)棧,系統(tǒng)將返回一個(gè)異常。這就是非可執(zhí)行 (No execute, NX) 在頁(yè)表中的作用。英特爾,AMD和ARM等廠商對(duì)該技術(shù)的稱呼并不完全一樣。
然后就是有意思的部分了:面向返回編程 (Return-orientated programming, ROP)。本質(zhì)上來講,你仍舊能夠覆蓋堆棧,用自己選擇的值進(jìn)行填充,但你的目的變成了創(chuàng)造一個(gè)全部指向運(yùn)行中程序有用指令塊的序列。對(duì)處理器而言,它仍舊在執(zhí)行正常的代碼,沒有拋出異常。
這樣理解它比較合適:這不是逐字逐句地按作者的原意讀一本書,而是選擇跳過其中的一些部分,這等于在原小說的基礎(chǔ)上進(jìn)行二次創(chuàng)作。這正是ROP的工作方式:你用小程序地址填充堆棧,每個(gè)小程序都以RET及其類似指令為結(jié)尾。當(dāng)處理器跳到某個(gè)小程序時(shí),它將執(zhí)行其中的指令,然后輪到RET,從堆棧中取出下一個(gè)返回地址,跳到其指向的下一個(gè)小程序。
以下是兩個(gè)小程序的例子:
pop %ebx
pop %eax
ret
mov %eax, (%ebx)
ret
第一個(gè)小程序從堆棧上取出兩個(gè)值,并在變量ebx和eax中存儲(chǔ)它們。別忘了,你控制著堆棧的內(nèi)容,因此能夠確保這些指令包含了你想要的數(shù)值。下一步,第二個(gè)小程序?qū)ax的數(shù)值填寫到ebx指向的內(nèi)存地址。將這些小程序連起來,你就能編輯當(dāng)前運(yùn)行中線程所有能夠改變的內(nèi)存地址,這被稱為任意寫入,它在篡改應(yīng)用和服務(wù)器方面非常有效。
最后,你將可以把足夠多的小代碼區(qū)域和參數(shù)縫在一起,要求操作系統(tǒng)將一個(gè)非可執(zhí)行的內(nèi)存區(qū)域標(biāo)記為可執(zhí)行,并保證它裝滿了你的惡意載荷,并跳轉(zhuǎn)到它。因?yàn)槟阋呀?jīng)將其標(biāo)記為可執(zhí)行,處理器運(yùn)行它時(shí)不會(huì)遇到什么問題,你也就完成了任意代碼執(zhí)行攻擊。
事實(shí)上,ROP和JOP就是惡意軟件作者用來獲取受害計(jì)算機(jī)控制權(quán)的基本方法。
CET在這里的功能是,當(dāng)從某個(gè)子程序返回時(shí),堆棧還沒有被惡意軟件所劫持。沒有ROP,漏洞利用就不會(huì)有效,也就不會(huì)有惡意軟件感染。
無法通過通常的程序代碼修改影子堆棧。當(dāng)然,如果你能以某種方式欺騙內(nèi)核,讓它解鎖影子堆棧,干涉它,讓它和你的ROP鏈條一致,然后重新啟用保護(hù),就能夠繞過CET。
需要反饋
英特爾網(wǎng)絡(luò)安全專家Matthew Rosenquist說:“英特爾開發(fā)的控制流強(qiáng)制技術(shù)為未來利用中央處理器的固定硬件架構(gòu),建立控制,幫助防止并干預(yù)代碼重用攻擊提供了一個(gè)方向。”
“通過使用影子堆棧、指針和其它機(jī)制,CET建立的機(jī)制能夠防止有人濫用合法代碼。”
目前,CET處于預(yù)覽階段,還有許多工作尚待完成。該規(guī)范是在微軟的協(xié)助之下完成的,已經(jīng)開始在小范圍內(nèi)測(cè)試,獲取技術(shù)反饋。在CET的設(shè)計(jì)之下,可能會(huì)有一些沒有考慮到的部分可能被利用,回避保護(hù)機(jī)制。它是個(gè)非常復(fù)雜的系統(tǒng),和許多芯片級(jí)的特性相關(guān),比如特權(quán)級(jí)別、中斷、管理程序入口和出口。在這項(xiàng)技術(shù)真的落實(shí)到產(chǎn)品上之前還有一段路要走。
前幾年就已經(jīng)有一些計(jì)算機(jī)科學(xué)家提出了影子堆棧的概念。在這項(xiàng)技術(shù)成為主流之前,抵抗ROP漏洞利用的主要方式是ASLR,也即地址空間隨機(jī)化。這一操作系統(tǒng)級(jí)的特性會(huì)在程序的虛擬空間中隨機(jī)為程序的組件分配地址,比如它的庫(kù)和可執(zhí)行文件。由于代碼存儲(chǔ)的空間在每次程序開始執(zhí)行之前都會(huì)被隨機(jī)化,小程序的位置也會(huì)改變,因此精心準(zhǔn)備的ROP棧將不再有效:它指向的是錯(cuò)誤的位置,會(huì)導(dǎo)致應(yīng)用或服務(wù)器崩潰。
ASLR并不是萬(wàn)無一失。有時(shí)黑客能夠通過信息泄露漏洞獲取小程序的基地址,進(jìn)而計(jì)算并確定其位置,實(shí)現(xiàn)跳轉(zhuǎn)。