利用 CPU 效能計數器來偵測 Meltdown 和 Spectre 漏洞攻擊

針對知名漏洞 Meltdown 和 Spectre 的攻擊目前或許仍在概念驗證 (PoC) 階段,或者如某些報導所說,仍在實驗性階段。不過,歹徒總有一天會完全掌握這些漏洞,只是時間早晚的問題。Meltdown 和 Spectre 漏洞的影響非常深遠,它最早可追溯至 1995 年所生產的電腦。此問題對企業來說尤其棘手,特別是受到歐盟通用資料保護法 (GDPR) 所規範的企業。

企業除了修補和更新系統之外,還有一點也很重要,那就是採取主動策略來搜尋、偵測、回應威脅,尤其像 Meltdown 和 Spectre 這類影響深遠的威脅。

針對 Meltdown 和 Spectre 漏洞攻擊,趨勢科技研究了一種利用 Intel 處理器內建效能計數器 (Performance Counter) 的特殊偵測技巧。這些計數器會記錄快取失誤的次數 (也就是應用程式所要求的資料不在快取記憶體當中的情況),這些數據可用來偵測 Meltdown 和 Spectre 漏洞攻擊。

由於這兩個漏洞都是利用今日 CPU 在設計上會為了提高效率而預先載入並執行一些指令以免 CPU 陷入無事可做的狀態。

這篇文章的目的,是希望系統管理和資安人員除了妥善訂定修補政策之外,還有另一種防範之道,尤其是那些因可能影響穩定性或效能而無法套用修補更新的系統。

請注意,MeltdownPrime 和 SpectrePrime 也可以利用偵測快取旁道攻擊 (cache-side channel attack) 的方式來加以偵測。儘管參數有些不同,但此方法可偵測 Flush + Reload 以及 Prime 和 Probe 方式的攻擊。只是,此方法雖已在 Linux 系統實驗成功,但尚未在 Mac 系統上測試過。

Spectre SGX (SgxPectre) 的目的是用來洩漏記憶體安全區域 (secure enclave) 內的資料。根據 Intel 的 SGX 程式設計手冊的說法,效能計數器在 SGX 安全區域內可能會被停用。不過,由於快取時間差攻擊是在 SGX 安全區域之外的非信任程式碼中執行,因此效能計數器仍會有關於快取命中 (Hit) 和失誤 (Miss) 的數據,所以應該還是有辦法偵測。關於這點,我們並未完整測試,因此無法百分之百確認。至於參數 (也就是取樣率、門檻等等) 則應視環境而有所差異。

Meltdown 和 Spectre 如何利用處理器預測執行的特性?
Meltdown 漏洞是利用 CPU 在預測執行時會無視權限規定,將原本沒有權限存取的記憶體資料載入快取當中,使得歹徒能夠利用快取旁道攻擊的方式取得這些資料。接著 CPU 才會發現使用者沒有適當權限,進而將運算結果拋棄。但此時已經載入的資料還是會留在末階快取 (Last-Level Cache,簡稱 LLC) 當中,所以才會讓駭客有機會取得這些資料。

請看以下這道指令:
mov rax, [forbiddenAddress]

當這行指令執行時,CPU 會去 forbiddenAddress (不應存取的位址) 讀取資料,進而產生記憶體分頁錯誤 (page fault),接著系統會發出 SIGSEGV (分段違規) 錯誤訊號,然後終止執行程序 (系統預設的處理方式)。不過,駭客會幫 SIGSEGV 錯誤訊號註冊一個自訂處理函式,這樣就能在不讓應用程式結束的狀況下讀取記憶體內容。不過這些錯誤訊號還是會在作業系統當中留下記錄。

只不過,這些記錄也可以用 Intel 的 Transactional Synchronization Extensions (TSX) 交易同步延伸指令集來清除,這些指令是用來讓 CPU 處理執行緒序列化的問題。基本上,駭客會使用 Restrictive Transactional Memory (RTM) 限制交易記憶體介面。Meltdown 攻擊程式碼 (如下所示) 的開頭和結尾分別會有 xbeginxend 兩道指令,這使得處理器不會產生分頁錯誤。如此可以少掉一些干擾,讓 Meltdown 攻擊執行時更加迅速:
xbegin
mov rax, [forbiddenAddress]

xend

Spectre 同樣也是一個利用 CPU 預測執行特性的漏洞攻擊。不過與 Meltdown 不同的是,Spectre 所讀取的是條件分支程式碼當中不應存取的記憶體位址。而這個條件分支基本上不應執行,但由於今日的 CPU 會利用分支預測器來預測接下來會執行哪一個分支,然後預先載入該分支的程式碼來執行。

我們以一段非常簡單的範例程式來說明 Spectre 的攻擊:

mov rax, [rbp-10] // rax eq.5
mov rbx, [rbp-18] // rbx eq.4

xor rax, rbx
je no_way

ret

no_way:
  mov rax, [forbiddenAddress]

駭客的目標是要讓分支預測器「猜錯」分支條件 (也就是 XOR 運算) 的結果,這樣 CPU 就會預先載入「no_way」那部分的程式碼來執行。當 CPU 發現其預測結果錯誤時,就會拋棄預測執行的結果。但駭客就能到快取當中去取得想要的資料。由於此一情況是完全發生在 CPU 內部,因此完全不會產生分頁錯誤。

除了上述方法之外,駭客還可以使用快取旁道攻擊的方式來取得資料,此時作業系統不會收到錯誤訊號。請注意,Spectre 漏洞的攻擊技巧難度更高,同時也因為不同 CPU 的分支預測器會有所不同,因此將受限於某些 CPU 型號才能適用。

利用快取失誤來偵測 Meltdown 和 Spectre 攻擊
由於 Meltdown 會因為分頁錯誤的關係而留下一些攻擊痕跡,因此我們可以藉由核心追蹤 (kernel tracing) 的方式來攔截分頁錯誤訊號,進而偵測攻擊。我們攔截的是作業系統的 SIGSEGV 訊號 (segfaults)。當某個執行程序產生太多的 segfaults,就會觸發我們的警示。

我們在 Linux 上使用 kprobe 工具來攔截 force_sig_info  事件。我們確定可以利用自訂的訊號處理函式來偵測 Meltdown 攻擊。此方法的誤判率相當低,因為單一執行程序產生大量 SIGSEGV 訊號的情況非比尋常,因此非常可疑。不過,如果駭客使用的是 TSX 指令,那就不會產生 SIGSEV 訊號,那這套偵測機制就無法發揮作用。

Meltdown 和 Spectre 都會使用快取旁道攻擊來讀取想要的資料,這在今日 CPU 的微架構設計下是可行的。但這樣的攻擊有辦法偵測嗎?快取的目的是為了減少記憶體載入的延遲,今日 CPU 都採用多階式快取架構,最快的是第一階 (L1) 快取,最慢的是第三階 (L3) 快取。而且快取採用的是大包小的設計 (Li⊂Li+1),也就是說,L1 的內容也包含在 L2 當中,L2 的內容也包含在 L3 當中。

此外,L3 快取還是各 CPU 核心所共用,因此不僅包含資料,也包含指令,所以更容易遭到攻擊。L3 快取是 CPU 在存取記憶體 (DRAM) 之前搜尋的最後一道快取,因此也包含 DRAM 位址對應資料。


圖 1:今日 CPU 架構:多核心和 L3 快取。

當 CPU 在讀取一個已經在快取當中的記憶體資料時 (快取命中),其速度遠比必須到 DRAM 當中載入 (快取失誤) 快上許多。理論上,駭客可判斷快取命中和快取失誤的情況,並且在攻擊時利用這一點來傳輸資料。依照邏輯,駭客攻擊時會造成快取失誤次數增加。但快取失誤次數是否可取得並用於偵測攻擊?如何區分惡意和正常的情況以避免誤判?

快取失誤的次數可透過硬體效能計數器來取得。事實上,Intel 處理器的效能計數器分為架構通用和型號特定兩種。架構通用的效能計數器在不同微架構之間是通用的,這些計數器從 Intel Core Solo 和 Intel Core Duo 處理器時代即已存在。

架構通用效能計數器可透過 「cpuid」 指令 (eax=0x7, ecx=0x0) 來檢查是否存在。根據我們的測試,以下是執行 intel_cpu_info 工具 (參數 -arch) 所得到的結果:
Printing architectural CPU Information:
Version ID of architectural performance monitoring = 4
Number of general-purpose performance monitoring counter per logical processor = 4
Bit width of general-purpose, performance monitoring counter = 48
Length of EBX bit vector to enumerate architectural performance monitoring events = 7
Core cycle event available: yes
Instruction retired event available: yes
Reference cycles event available: yes
Last-level cache reference event available: yes
Last-level cache misses event available: yes
Branch instruction retired event available: yes
Branch mispredict retired event available: yes
Number of fixed-function performance counters ((if Version ID > 1) = 3
Bit width of fixed-function performance counters (if Version ID > 1) = 48

針對末階快取 (LLC),請特別留意 LLC references (LLC 參照) 和 LLC misses (LLC 失誤) 兩個事件。根據 Intel 手冊的資料:

  • Last Level Cache References (末階快取參照)– 事件選取:2EH,Umask:4FH。此事件會記錄 CPU 核心參照 LLC 的次數。
  • Last Level Cache Misses (末階快取失誤)– 事件選取:2EH,Umask:41H。此事件會記錄 CPU 核心參照 LLC 時發生快取失誤的次數。

為了讀取 LLC 失誤次數,預先檢查效能計數器是否存在非常重要,尤其是在虛擬環境底下。此外,也需要 CPU 和作業系統核心支援,才能讀取這些數值,因為讀取這些數值的程式碼無法在使用者模式下執行 (必須在核心模式下執行)。此外還需要一個應用程式來從核心取得這些數值。


圖 2:檢查效能計數器是否存在所需的要素:CPU 支援、作業系統核心支援、應用程式支援。

在 Linux 環境下,我們可以使用「perf」效能分析工具。其他平台則可能需要藉由特殊的驅動程式。

只要執行「perf list」指令就能列出系統可提供的事件。LLC 參照與 LLC 失誤兩個項目分別稱為「cache-misses」和「cache-references」。此外,根據 Intel 的資料,也可以使用「perf stat -e r4f2e,r412e」這道指令來讀取 LLC 參照和 LLC 失誤的原始值。

其他 LLC相關的計數器還有「LLC-loads」(LLC 載入) 和「LLC-load-misses」(LLC 載入失誤)。不過這兩個計數器視 CPU 型號而定,因此在某些情況下無法取得。例如,如果電腦使用的是 Sandy Bridge 微架構的 CPU,就無法取得 LLC-load-misses 計數器,但 LLC-loads 還是沒問題。


圖 3:實體 CPU 的計數器有型號限制。

此外,VMware 虛擬機器也不提供這些計數器,就算啟用「Virtual CPU Performance Monitoring Counters」(虛擬 CPU 效能計數器) 選項也沒用,不過還是可以取得「LLC 參照」和「LLC 失誤」兩個數據。


圖 4:實體 CPU 的效能計數器有型號限制。

我們會建議先使用「perf stat」指令來測試看看。當情況允許時,LLC references、LLC misses 以及 LLC-loads、LLC-load-misses 四個計數器我們都會使用。此外,我們也測試了一些熱門雲端平台的 Linux 虛擬機器,但這些虛擬機器大多不提供或不支援上述計數器。

 

偵測測試
為了確認快取失誤是否真能用來偵測旁道攻擊,我們採用以下的 LLC 效能計數器及設定:

  • 我們針對每一個邏輯 CPU 設定了兩個 perf 事件 (LLC-references 和 LLC-misses),然後讀取每個 CPU 上所有執行程序和執行緒的相關數據。我們在溢流取樣期間「P」之後讀取計數器數值。接著我們比較 LLC 失誤率:
  • 當 MR > 0.99 時即代表偵測到攻擊。
  • 我們測試了兩個取樣期間:P1=10,000,P2=20,000。

我們使用實體機器測試了以下情況:

  1. 每道壓力測試指令跑兩分鐘 (指令中的「#」代表邏輯 CPU 數量)
    1. stress -c #
    2. stress -i #
    3. stress -m #
    4. stress -d #
    5. stress -c #
    6. stress -c # -i # -d #
    7. stress -c # -I # -d # -m #
  2. 使用 VLC 播放 4K 影片
  3. 發動 Meltdown 概念驗證攻擊
  4. 發動 Spectre 概念驗證攻擊

針對 LLC-references 和 LLC-misses 事件的測試結果如下:

實體機器 1
取樣期間:P1=10,000

  • 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
  • 播放 4K 影片會產生誤判。
  • 成功偵測到 Meltdown 概念驗證攻擊。
  • 成功偵測到 Spectre 概念驗證攻擊。

取樣期間:P2=20,000

  • 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
  • 播放 4K 影片「不會」造成任何誤判。
  • 成功偵測到 Meltdown 概念驗證攻擊。
  • 成功偵測到 Spectre 概念驗證攻擊。

圖 5:使用效能計數器來偵測 Spectre 概念驗證攻擊的範例。

實體機器 2:
取樣期間:P1=10,000
1) 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
2) 播放 4K 影片會產生誤判。
3) 成功偵測到 Meltdown 概念驗證攻擊。
4) 成功偵測到 Spectre 概念驗證攻擊。

取樣期間:P2=20,000
1) 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
2) 播放 4K 影片「不會」產生誤判。
3) 成功偵測到 Meltdown 概念驗證攻擊。
4) 成功偵測到 Spectre 概念驗證攻擊。

虛擬機器 1:
取樣期間:P1=10,000
1) 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
2) 未測試。
3) 成功偵測到 Meltdown 概念驗證攻擊。
4) 成功偵測到 Spectre 概念驗證攻擊。

取樣期間:P2=20,000
1) 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
2) 未測試。
3) 成功偵測到 Meltdown 概念驗證攻擊。
4) 成功偵測到 Spectre 概念驗證攻擊。

針對 LLC-loads 和 LLC-load-misses 事件的測試結果如下:

實體機器 1 – 該機器不提供這些效能計數器。

實體機器 2

取樣期間:P1=10,000

  • 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
  • 播放 4K 影片「不會」產生誤判。
  • 成功偵測到 Meltdown 概念驗證攻擊。
  • 成功偵測到 Spectre 概念驗證攻擊。

取樣期間:P2=20,000

  • 只有當壓力測試指令使用了「-m」參數時,才會產生誤判。
  • 播放 4K 影片「不會」產生誤判。
  • 成功偵測到 Meltdown 概念驗證攻擊。
  • 成功偵測到 Spectre 概念驗證攻擊。

虛擬機器 1 – 該機器不提供這些效能計數器。

以下是我們的測試環境:

  • 實體機器 1:Core i5-2430M @2.40GHz,Sandy Bridge,Ubuntu 14.04
  • 實體機器 2:Core i7-4600U @2.10GHz,Haswell,Ubuntu 14.04
  • 虛擬機器 1:VMware ESX VM,主機:Intel Xeon E5-2660 @2.2GHz,Sandy Bridge,Ubuntu 16.04

vpmc.enable = “TRUE”
vpmc.freezeMode = “vcpu”
誤判
根據我們的觀察,取樣期間會影響到是否產生誤判。當使用「stress -m」指令時,誤判一定會產生。我們查看了一下「stress」指令的說明文件發現:

-m, –vm N
會產生 N 個工作緒不斷呼叫 malloc()/free() 記憶體分配/釋放函式。
這一點不令人意外,因為我們之前提到 LLC 與實體記憶體彼此相關。因此,我們建議在記憶體分配頻繁的環境當中測試時要更加小心。

我們發現 LLC-loads 和 LLC-load-misses 兩項計數器的結果較為精準。不過 LLC references (cache-references) 和 LLC misses (cache-misses) 還是可以使用。

偵測
技巧
避免產生例外錯誤的方法保護誤判率針對虛擬環境攻擊是否可行? 
PCMTSX有保護
(如果虛擬機器內提供 TSX 延伸指令集;在我們測試的虛擬機器當中並未提供)
ktraceTSX無保護未測試
(如果虛擬機器內提供 TSX 延伸指令集;在我們測試的虛擬機器當中並未提供)
PCM條件分支有保護
ktrace條件分支無保護未測試
PCM無 (直接存取記憶體)有保護
ktrace無 (直接存取記憶體)有保護

圖 6:我們使用核心追蹤程式 ktrace 和效能監視器程式 (PCM) 的測試結果摘要;
每一種旁道攻擊技巧皆採用 Flush-Reload 的方式。
註:Spectre 和 Meltdown 皆使用條件分支概念驗證攻擊來避免產生例外錯誤 (exception)。在這種情況下 ktrace 無法提供保護或偵測攻擊。
絕無萬靈丹
採用核心追蹤與 SIGSEV 訊號的偵測技巧,確實可以偵測 Meltdown 漏洞攻擊 (只要系統環境「不提供」TSX-NI 延伸指令集),這一點視 CPU 而定 (亦即是否為 Haswell 微架構 的 Intel 處理器)。

目前已有些工具可以用來檢查 Intel TSX-NI 延伸指令集是否可用。根據 Intel 的 64 位元和 IA-32 架構軟體開發人員手冊,其中一種方式是使用 cupid 指令。

當系統環境可以提供 CPU 效能計數器資料時,就可以利用這些計數器來偵測快取旁道攻擊。在有安裝 perf-tools 的 Linux 上,要檢查系統是否可提供效能計數器,可使用這道指令:「perf stat -e -a cache-references,cache-misses,LLC-loads,LLC-load-misses」。大多數的虛擬環境 (Amazon AWS、Azure、Virtual Box) 預設都不提供硬體效能計數器。不過若是 VMware 的話則有選項可以啟用。

在其他平台上,如 Windows 和 macOS,如果要讀取效能計數器的話,需要花費更多功夫,因為這些計數器數值無法從使用者模式讀取。所以可能需要藉由適當的驅動程式來讀取這些數值,然後進行取樣並且取得造成快取失誤飆高的處理程序識別碼 (PID)。

此外,我們建議在使用這套偵測方法時要針對執行環境調校一些參數。此外我們也利用警示來做判斷 (隨使用者而異)。這樣的作法可提供執行程序識別碼 (PID) 和工作識別碼 (TID),如此使用者才能針對有問題的處理程序或執行緒採取行動。

同時,取樣期間也會影響偵測的敏感度:提高取樣率可以降低誤判,但如果駭客節奏抓得好的話,還是有可能躲過偵測,例如:讀取少量的資料之後就休息一段時間,這樣的做法固然會減緩攻擊速度,但一次讀取一大塊記憶體內容就會觸發警示。另一方面,較低的取樣率容易造成較多誤判。我們發現 VMware 當中的效能計數器沒有實體機器來得敏感。

經過驗證,只要能取得硬體的效能計數器資料,這套技巧就可以偵測採用 FLUSH+RELOAD 方式的快取旁道攻擊。不過,仍要針對執行環境進行一番測試和調校。

在偵測和防範 Meltdown 和 Spectre 漏洞攻擊方面,確實沒有一套萬用的解決方案。所以,防範時還要考慮多種因素,而偵測機制也要看個別環境是否提供所需的元素。

在面對不斷演變的威脅,資安意識與主動偵測固然重要,但縱深防禦也同樣重要。此外,採取一套主動的事件應變策略,也有助於偵測威脅攻擊過程的徵兆,讓企業更有效因應,尤其是防範像 Meltdown 和 Spectre 這麼影響深遠的漏洞。

 

原文出處:Detecting Attacks that Exploit Meltdown and Spectre with Performance Counters 作者:David Fiser 與 William Gamazo Sanchez