當前位置:
首頁 > 最新 > 使用靜態分析檢測Specter 「幽靈」漏洞攻擊

使用靜態分析檢測Specter 「幽靈」漏洞攻擊

聲明:轉載必須全文,否則必究。

在過去的幾個月里,Spectre(CVE-2017-5753和CVE-2017-5715)已經成為一種新的漏洞。為了幫助開發社區積極抵禦這些攻擊,SynopsysSoftware Ingegrity Group發布了新版本的Coverity靜態代碼分析檢查程序,能夠識別可能受到Spectre攻擊的代碼模式。 我們研究了Spectre可以利用的代碼類型,調整靜態分析技術來檢測它們。 在本文中,我們將討論我們發現的一些真實示例,並分享修復Spectre的技術。

背景知識

首先,讓我們簡要介紹一下理解Specter幽靈漏洞所需的幾個概念。 熟悉Spectre漏洞(分支預測,推測執行和緩存定時攻擊)細節的讀者可以直接跳到下面的「使用靜態分析」部分。

分支預測當處理器遇到條件分支指令時,它會嘗試預測將執行哪個分支。這個預測基於許多因素,例如條件中使用的值的範圍,之前執行的指令的歷史以及過去執行分支的次數。推測執行

作為優化手段,處理器推測性地提前執行指令,判定在實際評估條件之前將採用預測的分支。如果稍後處理器意識到未採用分支,則它將簡單地丟棄結果並繼續使用正確的分支指令序列。考慮以下代碼段:

if(x

val = array1 [x];

}

有人可能會假設只有當x小於array1_size時才會執行val = array1 [x]。 但是,如果array1_size的值不在高速緩存中,則處理器必須等到從主存儲器讀取值,這需要更長的時間。 處理器可以預測條件為真並且推測性地執行if語句的主體,而不是空閑,即使x在array1的邊界之外。Specter漏洞依賴於推測執行可能影響緩存狀態的事實。

緩存定時攻擊

高速緩存定時攻擊用於通過測量從內存地址讀取所需的時間來確定值是否在高速緩存中。如果值在緩存中,則讀取速度很快,如果不是,則速度很慢。攻擊者可以通過讀取一個非常大的數組,使用自己的數據填充緩存,然後運行訪問敏感信息的受害者代碼。要將自己的數據載入到緩存中,受害者的代碼必須驅逐一些攻擊者的數據。攻擊者現在可以再次運行自己的代碼,並通過計時內存訪問來推斷受害者訪問了哪些內存位置。Prime和probe以及flush and和load是眾所周知的緩存定時攻擊,用作Spectre攻擊中的數據提取機制。

Spectre 幽靈漏洞

Spectre是一個信息泄漏漏洞,它使用處理器的推測執行和數據緩存機制從受害者進程中提取敏感數據。回想一下,基於分支預測的推測性執行可能導致數據高速緩存的更新。如果未採用預測分支,則處理器丟棄寄存器的輸出以及其他狀態信息,但不丟棄對高速緩存的更新。攻擊者可以通過錯誤處理錯誤的分支來利用這一點,然後使用該推測執行的結果保留在緩存中以提取敏感數據的事實。此攻擊技術有兩種變體:變體1-邊界檢查旁路(CVE-2017-5753)和變體2-分支目標註射(CVE-2017-5715)。在這篇文章中,我們將重點關注變體1,因為它更容易被利用。與變體2相比,變體1是用戶級和系統級C/C ++程序中更常出現的模式。

在Spectre白皮書(the Spectre white paper)的以下代碼示例中,array1和array2都是位元組數組:

if (x

v = array2[array1[x]*256]

}

為了能夠利用此代碼,攻擊者必須能夠控制`x`的值。 攻擊者還必須操縱緩存狀態,以便滿足以下條件:

·array1_size不在緩存中

·array1的地址在緩存中

·要提取的秘密數據的位置在緩存中

·array2的地址不在緩存中

讓我們假設攻擊者設法將分支預測演算法誤認為if條件為真。 然後,攻擊者為x提供了一個惡意選擇的值,該值超出了array1的範圍,因此array1 [x]指向敏感數據。 由於array1_size未被緩存,因此處理器必須等待從主存儲器讀取array1_size;同時,它會推測性地執行array2 [array1 [x] * 256]內存讀取。 由於array2也不在緩存中,因此讀取將更新取決於array1 [x] * 256值的緩存位置。 一旦處理器完成了array1_size的載入,它將回滾推測執行,但是對緩存狀態的更改將保持不變。 攻擊者可以使用緩存定時攻擊來觀察在推測執行期間哪個緩存位置發生了變化,從而推斷出array1 [x]的值。 通過改變x的值,攻擊者可以因此推斷出受害者進程中任何內存位置的值。

使用Coverity靜態分析

接下來,讓我們看看Coverity靜態分析技術如何幫助開發團隊檢測易受Specter攻擊影響的代碼。 為了找到幽靈漏洞,我們擴展了Coverity靜態分析工具,以識別可能容易受到Spectre的邊界檢查繞過變體的代碼實例。 Synopsys的目標是為開發團隊提供一個檢查器,能夠有效地識別可利用的代碼模式,同時不會給用戶帶來太多的缺陷誤報。 我們與來自知名硬體供應商和內核開發人員的開發團隊合作,以確保分析結果有意義且有用。

Coverity的新版本檢查程序查找可能使敏感進程(例如,內核,虛擬機管理程序,瀏覽器)容易受到Spectre攻擊的代碼模式。 檢查器查找發生以下事件的執行路徑::

·將值x與某個變數進行比較(例如,x

·x用作訪問內存和讀取某些值y的索引

·然後使用y訪問另一個內存位置

代碼模式變體

雖然上面的事件序列直接對應於Spectre白皮書中提供的模式,但我們的檢查器不僅僅是語法匹配。 編寫等效代碼的方法有很多種,我們試圖對變體1中的模式變體進行修改。以下是我們用於查找更多代碼變體的一些技術:

·在任務之間跟蹤賦值(例如,val = x)

·通過二進位操作跟蹤值(例如,x + 1

·除if語句之外的條件語句(例如,對於(i = 0; i + x

·Dereferences和指針算術(例如,char * p = arr1 + index)

·內存訪問API(例如,memcmp)

·跨越多個函數和文件的內存訪問

這些技術能夠找到多種Spectre代碼模式變體(比如這篇文章中提到的相關代碼變體),類似於:

Void victim_function_v12(size_t x, size_t y) {

If ((x + y)

Temp &= array2[array1[x + y] * 512];

}

Void victim_function_v11(size_t x) {

If (x

Temp = memcmp(&temp, array2 + (array1[x] * 512), 1);

}

靜態分析技術的權衡

由於程序分析通常是一個不可判定的問題,因此靜態分析無法精確檢測所有可利用的代碼模式。為了解決這個固有的局限性,我們不得不對檢查器報告的內容做出一些啟發式決策。安全行業有一個口頭禪「結果越多,風險越少」,對於這個檢查器,我們試圖在過度使用可能容易受到攻擊的代碼位置時犯錯誤。 另一方面,報告太多問題會讓開發人員感到壓力,他們可能無法全部解決這些問題。 以下是我們所做的一些權衡:

污點數據跟蹤

一項特別具有挑戰性的任務是決定一個值是否可以受到攻擊者控制。雖然Coverity具有污點跟蹤功能,但準確地執行此操作會很慢,並且需要對可以產生用戶可控數據的API進行建模。 因此,我們不檢查索引值是否可以由攻擊者控制。 由開發人員自行決定檢查Coverity報告的潛在易受攻擊的位置,並確定該索引是否是用戶可控制的。

實際檢測案例:

我們在幾個真實的代碼庫上運行了Coverity Spectre檢查器。雖然檢查器不保證代碼是可利用的,但是它意味著問題的代碼模式能夠被標記出來。由開發人員來檢查缺陷報告並確定適當的補救措施。在我們的實驗中,檢查員平均每35,000行代碼報告1個缺陷。 這遠遠低於代碼中的分支指令總數以及開發人員要檢查的可管理數量的缺陷。

以下示例基於檢查器在實際代碼庫中找到的缺陷之一。我們選擇此示例來突出顯示我們的檢查器功能。檢查器可以檢測通過代碼檢查難以識別的許多代碼行上傳播的模式。為簡潔起見,代碼已縮短為僅顯示相關部分:

說明:如果攻擊者可以控制victim_function的x參數,則代碼可能容易受到Spectre的邊界檢查旁路變體的影響。 將x +偏移量與第35行上的data-> header.maxIndex進行比較。假設攻擊者可以弄錯分支預測器,即使x +偏移量,也將推測性地執行第36行上的數組訪問(index = array1 [x + offset]) 超出範圍。 這可能導致敏感數據存儲在索引變數中。 回想一下,攻擊者無法直接訪問它,但必須將其用作訪問內存的索引。index稍後用作第48行的索引:data-> array2 [index * 512]。 如果攻擊者可以確保data-> array2 [index * 512]不在緩存中,則將更新與敏感值索引對應的緩存位置。 使用緩存計時攻擊,攻擊者可能能夠檢索部分敏感數據。

修復Spectre 漏洞

識別易受攻擊的代碼只是工作的一半。找出修復漏洞的正確方法同樣重要。

Spectre漏洞遭受攻擊的根本原因是推測執行不會將更改還原到緩存。一個自然的解決方法是通過添加在比較語句之後序列化指令的特殊指令來禁用推測性執行。這告訴處理器按程序順序執行指令:「等到比較語句執行完畢,以確定要採用哪個分支。」Intel提出來一個解決方案.在以下示例中,_mm_lfence()指令確保不會以推測方式執行if語句:

if (x

_mm_lfence();

v= array2[array1[x] * 256];

}

但是在每個條件分支之後添加_mm_lfence()指令會導致各種性能下降。 因此,將這些指令僅添加到易受Spectre攻擊的代碼中非常重要,在這個時候研發團隊就非常需要Coverity檢查器,幫助開發人員找到這些易受攻擊的代碼模式。

結語

關於作者

韓葆(Bob Han),Synopsys SIG 軟體質量與安全產品線業務負責人/高級安全方案架構師。曾在Sun中國研究院為OpenSolaris操作系統搭建測試平台,專註軟體質量與安全測試領域,深入研究白盒測試、靜態分析、網路協議Fuzzing、滲透測試等技術。經歷了研發測試行業和軟體安全行業在中國從無到有,從小到大的整個過程。2013年作為中國區首席工程師加入Coverity(2014年被Synopsys收購),成功的將靜態分析技術引入中國,在2014-2017年四次作為中國質量競爭力大會演講嘉賓介紹代碼靜態分析與軟體安全測試技術,並多次被軟體安全領域高峰論壇如Qcon, OWASP, ItClub等邀請為嘉賓分享行業動態,致力於軟體團隊的研發測試與軟體安全平台搭建,改善軟體質量,提高軟體安全性。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 研發測試圈 的精彩文章:

TAG:研發測試圈 |