叢 眸,張 平,王 寧
(1.長春理工大學 計算機科學技術學院,長春 130022;2.陸軍裝甲兵學院 北京 100072;3.公安部第三研究所,北京 100142)
計算機技術的迅速發展在提高人類生產力的同時也給個人信息安全帶來挑戰。在計算機硬件設計上,由于硬件的自身設計缺陷而造成的安全問題頗受學術界關注,并且此問題從軟件層面難以徹底解決,從而致使個人信息泄露。側信道分析[1]就是一種利用計算機物理缺陷的攻擊方法,其通過分析計算機元器件泄露的電磁、電流、聲音、時間等信息來獲取計算機的私密信息。這種攻擊方式徹底顛覆了傳統攻擊理念,并且很難對其進行防御。
Cache 計時攻擊是一種側信道分析[1]方法。根據計算機存儲系統中多級層次存儲器的結構,Cache的讀寫速度遠高于內存。設計Cache 是為了減少CPU 訪問主存的次數,從而加快計算機的運算速度。所有進程在運行時都會使用到Cache,同時CPU 也會從Cache 中讀取指令和數據進行運算。雖然進程之間相互隔離,但是從硬件的角度分析,不同的進程使用同一個Cache,因此,Cache 存在泄漏進程信息的可能。此外,在CPU 中的兩條進程,其中一條進程能夠通過Cache 泄漏的信息窺探另一條進程的數據。Cache 計時攻擊[2-3]就是利用Cache 信息泄露,通過側信道分析方式非法獲取其他進程數據的一種攻擊方式。
側信道分析是目前信息安全領域最主要的安全威脅之一,如2018年爆發的處理器A級漏洞Meltdown(熔斷)[4]和Spectre(幽靈)[5]均對個人信息安全帶來了極大的挑戰,利用這種攻擊方式不僅對個人計算機造成威脅,同時還對云服務商的安全問題提出了新的要求。雖然能以軟件的方式給系統打補丁,但是基于這2 個漏洞的變種仍然可以實現攻擊,以打補丁的方式不僅不能完全封堵漏洞,而且還會損失芯片5%~30%的性能作為代價。造成這兩個漏洞的本質是利用Cache 這個共享部件,通過使用Cache 計時攻擊來獲取系統內核內容和其他進程信息。
在Linux操作系統中使用內核地址空間布局隨機化(Kernel Address Space Layout Randomization,KASLR)防護技術是為了提高系統安全性。在沒有使用KASLR 技術時,系統會把低位的虛擬地址默認分配給內核的物理地址,在這種情況下,內核的存放位置幾乎對攻擊者是透明的。而使用KASLR 技術將內核的物理地址隨機映射到虛擬地址中,就能保護內核映射情況不被攻擊者知曉,從而提高系統的安全性。在內核地址映射到系統虛擬地址的過程中,不可避免地要使用Cache,這就使Cache 計時攻擊獲取內核地址的偏移位成為可能。文獻[6]利用側信道分析對分支目標緩沖區(Branch Target Buffer,BTB)進行攻擊,突破了KASLR 防護,獲取了內核的地址和受害者進程信息。文獻[7]利用側信道分析對內存管理系統進行攻擊,繞過KASLR 并獲取系統特權,證實了該方法在X86 CPU 實現的可行性,并且適用于虛擬機,會給云服務帶來安全威脅。文獻[8]利用CPU 預測執行和亂序執行的漏洞,使用Cache 計時攻擊突破KASLR 防護并破解密碼算法等私密信息。
本文根據CPU 在處理數據時會預取指令的特點,將數據存放在Cache 中,利用Cache 計時攻擊對內核地址偏移位進行探測,獲取內核地址的虛擬地址偏移位,從而突破Linux系統的KASLR 技術防護,將內核地址暴露在普通用戶層面,致使系統處于高風險狀態。
計時攻擊的原理主要是根據訪問數據時間的長短,并利用計算機硬件設計的缺陷來實現攻擊。Cache 的設計目的是為了解決CPU 運算速度和內存訪問速度不對等的問題,但是Cache 具有共享特性,容易造成線程數據泄露[9]。Cache 計時信息可分為多種,采集部件容量越小,攻擊準確度越高。傳統的Cache 計時攻擊需要將整個Cache 清空來采集計時信息,容易受到其他進程影響,采集的信息噪聲大,導致在分析階段非常困難,攻擊效果較差。之后研究者對Cache 計時攻擊提出了多種改進方法,如將采集部件由整個Cache 改進為一個Cache 組,甚至可為一個Cache 行,使計時攻擊采集的計時信息更準確,其他進程影響逐漸降低,從而提高攻擊準確度,并且降低分析階段的難度。例如Evict+Time[10]、Prime+Probe[11]、Flush+Reload[12-14]、Evict+Reload[15-16]、Flush+Flush[17]攻擊模型,只需要清空一個具體的Cache 組或者一個Cache 行,而不再需要清空整個Cache 來采集計時信息。2018 年1 月爆出的Intel CPU 設計漏洞,主要原因仍在于Cache 泄露的計時信息,攻擊者通過改進的計時攻擊模型對Cache 進行攻擊來獲取用戶私密信息。
Cache 計時攻擊可分為基于時序驅動、基于訪問驅動和基于蹤跡驅動[18]的攻擊,本文以基于訪問驅動模型的攻擊為研究對象。模型的實現需要以下3 點假設:
1)受害計算機中存在攻擊者部署的間諜進程(Spy Process)。
2)攻擊者能夠獲取被攻擊進程的數據在Cache中的映射關系,并且能夠清除被攻擊進程在Cache中的數據。
3)攻擊者能夠通過計時手段來判斷訪問的數據是否在Cache 中。
實現攻擊的難點在于第3 步,通過計時手段來判斷數據是否加載到Cache 中,在Intel 處理器中,能夠使用rdtsc 或rdtscp 指令來獲取CPU 時鐘周期為精度的計時。圖1 所示為Intel i7-6700 CPU 中訪問一次Cache 數據命中與失效的時間,從中能夠明顯地區分訪問命中和失效,Cache 計時攻擊就是通過這種時間的微妙差異來實現攻擊。

圖1 訪問一次數據所需要的時間Fig.1 Time spent on accessing data once
由于操作系統的其他程序進程也需要訪問Cache,在探測內核地址的同時,難以阻止其他程序進程訪問攻擊地址,一旦攻擊地址被其他進程訪問,攻擊就會受到干擾,容易對結果造成影響,因此Cache 計時攻擊在分析階段時,就需要先排除噪聲等影響才能獲取正確的數據。
KASLR 是一種保持計算機內核地址的隱蔽性以防受到緩沖區溢出等攻擊的計算機安全技術。如果內核地址映射到一個固定的地址,攻擊者能夠在內存中跳轉到特定的函數位置以執行非法程序,獲取受害者的私密數據。2001 年,KASLR 技術首次被設計實現,到2002 年10 月,KASLR 技術被應用于Linux 內核地址隨機化實現過程中,相比于其他實現方式,KASLR 技術還提供了更多的熵,能夠增加攻擊者預測目標地址的困難性并阻礙攻擊。
如果沒有使用KASLR 技術,Kernel image 將會按照vmlinux 鏈接腳本中的鏈接地址去映射虛擬地址,而使用KASLR 技術,則會將Kernel Image 再次映射到虛擬地址中,此次映射將會按照鏈接地址+offset 的新地址。由于Offset 會隨著計算機的每次開機而隨機變化,因此Kernel Image 映射到虛擬地址每次都會不同,從而實現內核地址隨機化。
KASLR 技術能夠有效防止系統內核受到攻擊,內核負責進程、內存管理、硬件I/O 等操作,沒有root權限的用戶不會影響到內核程序的正常運行,從而能夠確保內核運行處于一個安全穩定的狀態。內核空間是共享的,如果KASLR 技術被突破,將直接暴露內核地址,使攻擊更為簡單,通過攻擊能夠使普通用戶提權,監控內核中運行的程序,直接獲取用戶輸入的賬戶、銀行卡號、手機號等秘密信息,并且控制內核后還能直接破壞系統的正常運行,刪除、更改、破壞用戶重要數據,給社會安全帶來嚴重的隱患。
CPU 的發展給計算機帶來了巨大的變革,但硬盤的存儲速度與CPU 處理速度存在巨大的差距,傳統硬盤無法滿足CPU 的處理速度,從而催生Cache存儲,以彌補存儲速度之間的差距[19]。雖然Cache能夠彌補存儲速度之間的差異,但是CPU 訪問數據發生一次Cache 失效也會帶來巨大的開銷。為提高CPU 的性能,數據預取技術被提出,其在可能會發生Cache 失效之前提前將數據加載到Cache 中,以避免發生Cache 失效,從而提高CPU 性能,并且預取指令只對數據有效,而對指令預取無效。
Cache 預取實現方式可分為硬件實現和軟件實現[20]。硬件實現是通過專門的硬件來監控程序正在執行的指令或處理的數據、預測程序下步需要的數據或指令并提前預取到處理器中;軟件實現是利用編譯器對代碼進行分析,通過在程序編譯時往代碼段中插入prefetech 指令,在執行過程中以觸發數據或指令預取操作。軟件實現的優點是不會阻塞內存操作,并且不會改變CPU 狀態或導致頁錯誤,缺點是預取指令同樣需要消耗CPU 時鐘周期。本文主要以預取數據軟件實現為研究對象,在Intel 處理器中,軟件實現的預取指令主要有prefetcht0、prefetcht1、prefetcht2、prefetchnta 和prefetchw,這5 種預取指令的特性如表1 所示。

表1 預取指令的特性Table 1 The characteristics of prefetch instructions
一旦數據被預取到Cache 中,就能夠避免發生Cache 失效,從而提高CPU 的性能,但另一方面,內核地址映射預取到Cache 中,能夠通過計時攻擊得到內核地址映射的offset,從而可以更容易地觸發其他類型的攻擊。
與Flush+Reload 攻擊模型不同,本文使用的Cache 計時攻擊不需要重新加載數據和監控其他進程的信息,并且不需要任何特權,而只需要簡單地從Cache 中驅逐指定虛擬地址的數據,然后再次計算預取該地址數據的時間。如圖2(a)所示,普通程序分配的虛擬地址數據被加載到Cache 中,同樣地,隨機化的內核地址數據也被加載到Cache 中。由于沒有root 權限,使用簡單的匯編指令沒有權限將內核地址數據從Cache 中驅逐。本文方法利用這一點對KASLR 技術的內核起始位置進行探測,如圖2(b)所示。當能夠明確無法驅逐內核地址數據時,表明內核地址數據依然存在于Cache 中,如圖2(c)所示。再次預取指定數據時,通過使用rdtsc 或rdtscp 指令對預取數據進行計時,得到預取數據所消耗的時間,對所消耗的時間進行分析,則能得到內核地址的起始地址。

圖2 本文Cache 計時攻擊模型Fig.2 The proposed Cache timing attack model
筆者通過對Intel手冊進行分析發現,在Intel CPU中,預取“未映射到物理頁面的地址”會導致不確定的性能損失。對此,可以利用預取指令時所需要的時間消耗來探測內核映射的起始地址,使得Cache計時攻擊成為可能。
首先將指定地址數據從Cache 中驅逐,保證再次預取普通程序數據時,該數據是未緩存到Cache中的,然后使用rdtscp 指令對預取數據進行計時。本文方法中預取指令使用prefetcht2,因為在Intel CPU多級Cache 存儲結構中,只需要使用prefetcht2 指令將數據預取到L3 Cache 中就能達到攻擊效果,不會對Cache 造成污染,同時能夠提高攻擊效率。攻擊Linux 內核地址的步驟如下:
步驟1將數據從Cache 中驅逐。
步驟2觸發prefetch2 指令并計算指令消耗周期。
步驟3使用rdtscp指令計算預取每一行地址數據的時間消耗。
對于開啟KASLR 防護的Linux 操作系統,無法猜測內核的起始地址,只有通過遍歷內存地址來探測每一個可能的起始地址。一個Cache 行的大小為64 Byte,在攻擊過程中,傳入地址的遞增梯度為64 Byte,然后依次循環探測地址,直到找到內核地址偏移位為止。攻擊過程中需要探測每一個內存地址,如果按照64 Byte 的遞增梯度,攻擊的精度能精確到Cache 行,但是這種方式將會消耗大量的時間,在分析prefetcht2 預取指令時,預取指令預取數據時可以只加載一個Cache 行,也可以設置加載一個內存頁到Cache 中,并且KASLR 在映射內核地址時會4K 對齊。在攻擊實現時,本文將預取數據的大小設置為一個內存頁,如果梯度仍設為64 Byte,在一個內存頁的范圍內預取數據消耗的時間都會很短,從而造成不必要的地址探測。鑒于此,攻擊實現以一個內存頁大小為遞增梯度,以加快探測內核地址偏移位的速度,在Linux 操作系統中,默認的內存頁大小為4 KB,一個內存頁的數據需要64 個Cache 行來存儲,使用內存頁作為遞增梯度不僅不會影響計時攻擊的準確率,而且能夠大幅提高攻擊的效率。
攻擊實現在普通用戶環境下實現,在遍歷的一個地址時,由于普通用戶權限受限問題,無法從Cache中驅逐Linux系統內核的數據,但是可以正常驅逐其他程序的數據,因此,攻擊的實現不會受到用戶權限的影響。在預取數據時,其他程序地址數據預取時消耗CPU 周期較長,預取內核地址數據時較短,如果探測到內核地址的偏移位,預取數據消耗周期會出現跳躍并且會連續一段地址,然后通過分析消耗時間的差異以確定內核數據映射的起始位置。
獲取內核地址后,能夠更使緩存溢出、旁路攻擊、提權等攻擊方式更容易實現。在提供prefetch2指令的Intel CPU 中,這種攻擊很容易實現,盡管攻擊的效率不高,但是能夠準確獲取內核地址,并且受影響的范圍廣。針對這種攻擊方式,可設計相應的對抗方法,如利用控制精確計時、使用權限、監控Cache 驅逐率等方式來阻止攻擊。
通過攻擊實驗來驗證本文方法的有效性。利用計時攻擊來獲取內核地址的偏移位,為驗證計時攻擊的可行性,在同一硬件環境下對開啟KASLR 和未開啟KASLR 的Linux 操作系統進行測試,并在不同硬件環境下的計算機中進行攻擊測試,以驗證計時攻擊的普適性。攻擊實驗計算機硬件環境CPU為Intel i7-4720HQ和Intel i7-6700,兩臺計算機的CPU 都是4 核8 線程,且L3 Cache 大小分別為6 MB 和8 MB;實驗環境內存為4 GB,攻擊目標為Ubuntu 16.04 版本,計時攻擊的實現在普通用戶權限下進行。
在實際環境中,無法確認內核地址偏移位的大概位置,攻擊時采用暴力遍歷法,通過遍歷每個地址來判斷內核地址的偏移位。攻擊第一步需要將傳入的探測地址中的數據清除掉,使用clflush 指令將傳入的地址從Cache 中驅逐,由于遞增地址為4 KB,因此每次驅逐數據時需要驅逐以傳入地址為起始地址的一個內存頁大小。
在未開啟KASLR 防護的Ubuntu 操作系統中,內核映射的起始位置為0x00000000,在32 位操作系統中,前1 GB 大小虛擬地址空間的內存預留給內核,剩下的地址空間用戶使用。攻擊起始地址為0x00000000。如圖3 所示,每一行數據表示一個虛擬內存地址,每一列數據表示一次預取數據消耗的CPU 時鐘周期。每一個地址探測實驗進行12 次,結果如圖3 中各列所示。從地址0xc0000000 開始,后續的地址數據預取時所消耗的周期明顯增加,由此可以推測0xc0000000 為內核映射的尾地址,與真實情況下的內核地址映射是一致的。實驗結果表明,對于沒有開啟KASLR 防護的Linux 操作系統,計時攻擊能夠準確探測內核地址。

圖3 針對未開啟KASLR 防護系統的攻擊結果Fig.3 Attack result for Linux system without KASLR protection
在開啟KASLR 防護的Ubuntu 操作系統中,驗證計時攻擊探測內核地址的可能性。實驗環境與在攻擊未開啟KASLR 防護的環境保持不變,攻擊起始地址為0x00000000,遞增梯度為4 KB。圖4 為計時攻擊發現內核地址映射偏移位的部分截圖,在攻擊地址0x1c000000 時,數據預取時間周期明顯縮短,并且預取連續地址消耗的時鐘周期很短,因此,推測內核映射的offset 為0x1c000000。

圖4 針對開啟KASLR 防護系統的攻擊結果Fig.4 Attack result for Linux system with KASLR protection
為驗證計時攻擊的可靠和普適性,在其他CPU中重復進行上述實驗,探測出不同環境下的內核地址映射的偏移位。因為開啟KASLR 技術的操作系統在每一次開機都會重新映射內核,偏移位會發生變化,因此將同一臺機器在兩次開機情況下進行,比較內核映射偏移位的區別。如表2 所示,對不同CPU 中進行攻擊,每臺機器都進行兩次實驗,并且兩次獲取的內核映射偏移位都不同,經過多次實驗獲取內核地址偏移位,并在操作系統的root 環境下獲取內核地址偏移位進行比對。結果表明,計時攻擊探測出Ubuntu 16.04系統內核地址映射偏移位的成功率為100%。

表2 在不同CPU 設備下的攻擊結果Table 2 Attack results under different CPU devices
在使用KASLR 防護技術的Linux系統中,雖然內核地址能夠得到有效防護,但存在CPU 預取指令會泄露計時信息的不足。本文利用這一漏洞,設計一種計時攻擊方法來獲取Linux 內核映射地址。實驗結果表明,本文攻擊方法能夠準確獲取內核映射偏移位,并且可在不同CPU 設備中實現。因為Cache 作為所有進程共享的部件,所以計時攻擊方式能夠突破虛擬機之間的隔離,達到窺探其他主機內核數據的目的,對云服務商造成嚴重威脅。本文攻擊實現需要遍歷探測內存地址,效率較低,下一步將分析CPU 亂序執行和預執行的特性,對計時攻擊模型進行改進,以提高攻擊效率。同時針對這種攻擊方式提出相應的安全解決方案,以確保信息安全。