一、 引言

在不同的程序中,類似的漏洞會反復出現,原因之一是對代碼的重用,這導致重用代碼中安全漏洞的傳播;除此以外還有因為開發人員在實現相同的標準概念(如數學公式、物理定律、協議或語言解釋器)時經常犯類似的錯誤或是由于編程語言復雜的底層語義導致的常見誤解,使得語義上相似的漏洞經常在獨立開發的不相關代碼庫中重復出現。

盡管研究人員已經開發出許多成功的技術來檢測重復出現的安全漏洞,但現有的方法在幾個方面存在局限性。基于代碼相似度的方法旨在通過代碼重用來檢測重復漏洞。它們在預定義的邊界(例如,文件或函數)內生成已知漏洞的簽名,并將新程序中的語法模式與簽名進行比較。這些方法是高度精確的、可擴展的和通用的,因為它們的方法是基于語法匹配的。但它們通常無法檢測出語法結構完全不同但根源相同的已知漏洞的變體。另一類基于模式的靜態分析會評估目標程序的語義,并考慮它們的語法模式,這使得分析器能夠檢測具有與已知漏洞的程序相似的語法和語義特征的漏洞。但是設計這樣的分析需要靜態分析的專業知識,并且會帶來不小的工程負擔。

因此作者提出了Tracer,它以一般的污點分析為基礎,針對各種安全漏洞,如整數溢出、下溢、格式字符串、緩沖區溢出、命令注入等。在具有已知漏洞的代碼庫上運行靜態分析器,分析器會檢測從不受信任的輸入(source)到安全敏感函數(sink)的潛在易受攻擊的數據流,并在分析結果中識別實際的漏洞。然后提取漏洞從source到sink的數據依賴關系的軌跡。這些軌跡被編碼為特征向量,形成漏洞的簽名。一旦分析了一個新程序,Tracer提取程序中所有報告的警報的痕跡,并以相同的方式得到它們的特征向量,然后使用余弦相似性將警報的特征向量與已知易受攻擊的軌跡的特征向量進行比較,最后提供了一個按相似性排序的警報列表。

二、 概述

Tracer的基本框架如下圖,主要分為三個部分:

01通過靜態分析獲取潛在漏洞的軌跡

02從軌跡中提取特征向量

03已知漏洞的程序提取的向量將作為前面直接存儲在數據庫中,測試程序提取的向量會與數據庫中的向量進行相似度檢測

圖1 Tracer總體框架

三、詳細流程

1. 污點分析

Tracer基于通用的污點分析,計算從不可信的輸入(source)到敏感函數(sink)的潛在數據流,這些數據流帶有一個簡單抽象域:T ={⊥t,?t},用來表示值沒有被污染(⊥t)或是可能被污染(?t)。

為了更準確地分析,對于過污染overflow和欠污染underflow等情況,Tracer增加其他的抽象域來進行更詳細分析。例如針對overflow,使用一個簡單的抽象域I ={⊥o,?o}來估計一個整數值是否有可能溢出,一個不可信的輸入值最初是被污染的(?t),但不會溢出(⊥o)。一旦該值被用作可能引入整數溢出(例如,+、<<)的操作符的操作數,結果就會變為被污染(?t)和溢出(?o),對于malloc的情況,分析器只在抽象值同時被污染(?t)和溢出(?o)的時候發出警報,這樣可以在有效地計算惡意數據流的同時避免報告假警報。

在這一階段會得出一個警報集合,每個警報有兩個元素,c2為程序中的敏感函數(sink),c1為會影響到c2的不可信輸入(source)。

2. 從數據依賴圖提取軌跡

在污點分析獲得程序中潛在惡意流后,Tracer會提取程序的數據依賴關系圖,并根據每個警報的信息提取易受攻擊的軌跡,如圖2所示,軌跡包含了不受信任的數據從被讀入到被函數使用過程中所有的語句。

圖2 數據依賴圖示例

3. 特征表示

接著Tracer會將每個軌跡編碼成為整數特征向量,特征向量由兩部分組成:低級特征和高級特征。低級特征表示軌跡中基本操作符,比如*、<<等,還有常用的API,比如strlen等,這部分會根據特征出現的頻率形成向量,例如圖2(a)中的軌跡最后會得到向量<1,3,3,2,1,1,1,1>。此外還有5種高級特征來描述軌跡的詳細行為,用來判斷軌跡中是否出現了大于、大于等于、小于、小于等于、等于、不等于、等于百分比等表達式,假設在目標程序的軌跡中存在這樣的表達式,但在簽名軌跡中不存在,則會認為目標軌跡是安全的,相似度得分降低。

圖3 特征設計

4. 相似度檢查

相似度檢查會使用余弦相似度來計算程序提取的所有向量與數據庫中的所有簽名向量之間的相似性,得出相似性排名。例如圖2中提取出的兩個向量相似性結果為

(<1,3,3,2,1,1,1,1>·<1,3,3,2,1,0,0,1>)/(∥<1,3,3,2,1,1,1,1>∥∥<1,3,3,2,1,0,0,1>∥)=0.96.

四、實驗

論文設計了以下幾個問題:

RQ1: Tracer能否有效發現未知的重復漏洞?

RQ2: 與現有方法相比,Tracer的準確性如何?

RQ3: Tracer的高級特征是否有效?

RQ4: Tracer對大型程序的可擴展性如何?

1. 實驗設置

作者在Facebook的Infer分析器上實現了Tracer,使用由Infer的緩沖區溢出檢查器計算的指針信息。遵循Infer的框架,將污點分析設計成一個模塊化的過程間分析。污點分析檢查第4節中描述的五個常見漏洞:整數溢出、整數下溢、緩沖區溢出、命令注入和格式字符串錯誤。

用于當作簽名的程序有3個來源:(1)真實世界的漏洞:從CVE報告和之前的工作中收集了16個漏洞。(2)Juliet測試套件:由大量的小程序集組成,每個集里的小程序都有一個共同的漏洞,總共使用了5383個程序。(3)網絡教程:從OWASP提供的安全編程在線教程中收集了5個示例。用于檢測的程序使用了273個用C/C++編寫的Debian軟件包。

對比工具則選用了VUDDY[1], CCAligner[2],Devign[3]和CodeQL[4]

2. RQ1: 有效性

作者手動檢查了所有相似度大于0.85的警報,在67個軟件包中發現了112個新漏洞,截至寫文章時,發開人員已經確認了30個漏洞,分配了6個CVE。漏洞如表1所示。

表1 Tracer檢測到的新漏洞列表

表 2 各個工具對比

表2前三行顯示,大多數真正例在報告中排名很高。底層靜態分析報告1,975個警報,其中只有176個警報的得分高于0.95 (Tracer95)。其中真正例154個(87.5%)。如果將閾值分別設置為0.9 (Tracer90)和0.85 (Tracer85),則真正例率仍然很高(85.7%和78.1%)。Tracer基于相似性的評分有效地過濾掉了大量誤報,同時保留了許多真正的bug。實驗結果表明,該方法能夠有效地檢測出語義上重復出現的漏洞。特別是,由靜態分析提供支持的基于軌跡的相似性度量對語法變體具有魯棒性。因此,即使兩個程序具有明顯不同的語法特征,Tracer也可以報告具有高相似性得分的重復漏洞。

3. RQ2: 比較

(一)與VUDDY和CCAligner比較,如表2所示,VUDDYO報告的12個警報中有7個誤報,報告了5個函數克隆問題,其中檢測出了3個漏洞。VUDDYS報告了10個假警報,CCAligner報告了150個函數克隆,但沒有檢測到任何漏洞。由于CCAligner不是設計來查找漏洞的,因此一些不具有安全敏感調用的函數也會被報告。VUDDY和CCAligner都無法檢測到大多數由Tracer檢測到的重復漏洞。這主要是因為它們基于函數級粒度的語法相似性度量。然而,Tracer檢測到的漏洞大多涉及多個函數,并且語法結構與簽名有很大不同。現實世界程序中的這些特征阻礙了這些工具檢測語義上反復出現的漏洞。

(二)與CodeQL比較,CodeQL報告了來自基準程序的3,488個警報。作者檢查了其中324個警報,Tracer可以有效地檢測到CodeQL遺漏的重復漏洞,CodeQL報告了161個真警報(35.5%),而Tracer85檢測到253個真警報(55.8%),但是都沒有檢測到常見的漏洞,原因可能有三點:bug可以通過底層分析和CodeQL檢測出來,但是Tracer會過濾掉它們,因為在簽名數據庫中沒有類似的軌跡;只有CodeQL可以檢測到bug,但由于作者執行的不健全,底層分析遺漏了bug,特別是作者只實現了在他們的簽名數據庫中觀察到的外部庫(例如,read, getenv)的語義而CodeQL支持更廣泛的庫模型;只有我們的分析才能檢測到bug,但CodeQL無法檢測到,作者推測,這主要是因為它們作為通常的靜態bug檢測器的設計選擇不合理。在被檢查的324個警報中,CodeQL報告163個假正例(50.3%),而Tracer85報告71個(21.9%)。

(三)與Devign比較,作者將Devign實例化為兩種設置:DevignO使用作者提供的訓練數據進行訓練;DevignS使用簽名庫中的函數進行訓練。我們抽取了10000個易受攻擊的函數和10000個非易受攻擊的函數來訓練DevignS。DevignO只檢測到了10個漏洞,而DevugnS沒有報告任何漏洞,并且在這10次警報中,有8次Tracer85也檢測到了。

4. RQ3: 高級特征的影響

如表3所示,Tracer的性能從根本上不受高級特征選擇的限制。當高級特征被禁用時,閾值為0.95的Tracer仍然報告相同的一組警報。但是高級特征能幫助較低閾值的Tracer有效地過濾掉假警報。當使用高級特征時,閾值為0.85和0.90的Tracer報告的假正例減少10.1%和8.6%,同時保留了所有的真警報。

表3 使用不同特征的Tracer準確率

5. RQ4: 可擴展性

作者測量了每個基準的靜態分析和相似度檢查的總計算時間。結果表明,Tracer可擴展到大型程序,對于每個包,靜態分析平均需要140.42秒。特征向量構建和相似度計算的時間最多為2.71秒,與整個過程相比可以忽略不計,盡管大多數包的分析在20分鐘內完成,但有些包的分析時間要比平均時間長得多。例如hugin大約需要53分鐘,這主要是因為函數指針解析的不精確,導致通過虛假的間接調用分析過多的函數。另一個例外的例子是gettext,它只需要91秒,而它包含982K行代碼。盡管代碼規模巨大,但程序是由大量小型庫函數組成。因此,模塊化分析可以高度并行化。

圖4 Tracer在不同大小程序下的運行時間

五、 總結

文章提出了Tracer,一個用于檢測語義上重復出現的漏洞的框架。Tracer基于靜態分析,可以發現目標程序中潛在的易受攻擊的軌跡,然后將每個候選軌跡與從各種來源收集的已知漏洞進行比較。作者的實證研究表明,Tracer可以準確地從各種開源程序中檢測到語義相似的漏洞,作者預計Tracer將允許開發人員在不需要靜態分析專業知識的情況下輕松地防止重復出現的漏洞。