熊可欣,李 濤,余 琴,喬夢晴
(1.武漢科技大學(xué) 計算機科學(xué)與技術(shù)學(xué)院,湖北 武漢 430065;2.智能信息處理與實時工業(yè)系統(tǒng)湖北省重點實驗室,湖北 武漢 430065)
隨著軟件的復(fù)雜性不斷提高,漏洞的形態(tài)變得多樣化,軟件漏洞檢測技術(shù)需求不斷增加。為了實現(xiàn)漏洞檢測的高準(zhǔn)確率、低誤報率,大量的深度學(xué)習(xí)方法被嘗試用于檢測源代碼漏洞問題中,以實現(xiàn)漏洞檢測的自動化和智能化[1-3]。在目前的漏洞檢測中,重點關(guān)注于單個文件內(nèi)部是否存在漏洞。然而在實際的項目開發(fā)中代碼規(guī)模大,存在多種復(fù)雜的函數(shù)調(diào)用與參數(shù)傳遞,在單文件范圍內(nèi)有較好的檢測效果,然而檢測過程中都忽略了文件與文件之間可能存在的調(diào)用關(guān)系以及可能由于該類調(diào)用關(guān)系而產(chǎn)生的漏洞[4-5],多文件間的函數(shù)調(diào)用關(guān)系而產(chǎn)生的漏洞危害性較高但關(guān)注度較低,檢測難度較高[6-8]。因此,該文將關(guān)注多文件間的函數(shù)調(diào)用,檢測因調(diào)用關(guān)系而導(dǎo)致的漏洞。
在基于源代碼的漏洞檢測中,需要將源代碼中的有效部分提取出并進行抽象表示,再進行向量化處理供給模型訓(xùn)練[9-11]。目前代碼的特征表示方式可以大致分為文本、序列、圖、抽象語法樹和混合五種,不同的代碼表征方式有其不同的優(yōu)勢[12-13]。
文獻[14-16]將文本表示和深度學(xué)習(xí)相結(jié)合,從源代碼中提取有效的特征信息,利用詞頻統(tǒng)計方法構(gòu)建特征向量,采用深度學(xué)習(xí)對特征向量進行學(xué)習(xí)和訓(xùn)練。但普通的詞法分析忽略了代碼的上下文結(jié)構(gòu),無法確定代碼內(nèi)的函數(shù)調(diào)用關(guān)系。
序列表征是在源代碼基礎(chǔ)上提取字符流相關(guān)的標(biāo)識符、函數(shù)名等關(guān)鍵特征信息,同時也包含一定的函數(shù)調(diào)用和語句調(diào)用等信息。在文獻[17-20]通過序列表征進行漏洞檢測,從函數(shù)調(diào)用序列出發(fā),采用深度學(xué)習(xí)自動獲取序列特征。文獻[18]利用雙向長短期記憶循環(huán)神經(jīng)網(wǎng)絡(luò)構(gòu)建了一個VulDeePecker漏洞檢測系統(tǒng),文獻[19]在文獻[18]的基礎(chǔ)上添加了控制依賴關(guān)系,但對全局特征和局部特征存在一定的學(xué)習(xí)偏差,文獻[18-19]誤報率都較大。文獻[20]在程序執(zhí)行過程中進行收集函數(shù)調(diào)用序列作為特征用來訓(xùn)練模型,可以更好地挖掘出更多的特征信息。
抽象語法樹是源代碼抽象語法結(jié)構(gòu)的樹狀表現(xiàn)形式,樹的每個節(jié)點中都包含源代碼的語義和結(jié)構(gòu)信息。文獻[21]在抽象語法樹的基礎(chǔ)上進行項目內(nèi)的漏洞預(yù)測?;诔橄笳Z法樹的檢測可以在節(jié)點上進行標(biāo)記,可以對漏洞的定位工作有一定的幫助。然而對于多文件之間的函數(shù)調(diào)用,抽象語法樹的生成時間和空間復(fù)雜度都較高,難以適用于規(guī)模較大的系統(tǒng)。
基于圖的表征方式可以通過圖結(jié)構(gòu)表示源代碼的語義和詞法,能夠有效保留代碼的上下文結(jié)構(gòu)信息。相比于抽象語法樹的表征方式,圖表征中包含了更多的源代碼內(nèi)部結(jié)構(gòu)信息。文獻[22]提出獲取Sink函數(shù)調(diào)用的代碼子圖,Sink函數(shù)即在源代碼中可能導(dǎo)致漏洞的調(diào)用函數(shù),但該檢測僅局限于Sink函數(shù)內(nèi)部有無漏洞,無法檢測無漏洞文件間的函數(shù)調(diào)用導(dǎo)致的漏洞。文獻[23]在代碼屬性圖的基礎(chǔ)上,學(xué)習(xí)圖的局部和全局信息,利用程序切片技術(shù)簡化圖結(jié)構(gòu)。
該文的漏洞檢測重點關(guān)注多個無漏洞文件間因函數(shù)調(diào)用和參數(shù)傳遞而導(dǎo)致的漏洞,因此在將JAVA源代碼轉(zhuǎn)化為程序依賴圖(PDG),保留源代碼的上下文結(jié)構(gòu)關(guān)系,提出了一種通過圖節(jié)點信息觀察函數(shù)調(diào)用關(guān)系、融合多文件的圖特征為PDGcross特征、再進行深度學(xué)習(xí)的模型訓(xùn)練和預(yù)測的漏洞檢測方法,實現(xiàn)了檢測文件間函數(shù)調(diào)用而產(chǎn)生的漏洞。與傳統(tǒng)的漏洞檢測相比,該文的創(chuàng)新點在于更關(guān)注文件之間的函數(shù)調(diào)用關(guān)系,實現(xiàn)檢測因函數(shù)調(diào)用和參數(shù)傳遞而導(dǎo)致的該類漏洞的高準(zhǔn)確率和低漏報率。
提出的基于PDGcross特征和LSTM模型的漏洞檢測方法的整體架構(gòu)如圖1所示。
圖1 基于PDGcross的源代碼漏洞檢測整體架構(gòu)
首先,將源代碼通過開源工具Sourcedg[24]轉(zhuǎn)為程序依賴圖(PDG);在程序依賴圖的基礎(chǔ)上遍歷圖節(jié)點,判斷代碼中有無跨文件的函數(shù)調(diào)用事件,若無則不做處理,若有函數(shù)調(diào)用事件則需要確定被調(diào)用文件,形成一個代碼群;在當(dāng)前文件所擁有的代碼群中,通過數(shù)據(jù)流分析和控制流分析,融合被調(diào)用文件的節(jié)點,通過節(jié)點間數(shù)據(jù)依賴關(guān)系和控制依賴關(guān)系添加邊信息,為當(dāng)前文件形成一個新的PDGcross圖表征,在PDGcross的基礎(chǔ)上利用圖嵌入將圖表征轉(zhuǎn)為特征矩陣,訓(xùn)練LSTM模型實現(xiàn)跨文件的漏洞檢測。
在目前的圖表征中,常用的圖表征包含數(shù)據(jù)流圖、控制流圖、程序依賴圖(Program Dependence Graph,PDG)、數(shù)據(jù)依賴圖和數(shù)據(jù)屬性圖等,不同的圖表征結(jié)構(gòu)包含的源代碼信息各有不同。程序依賴圖主要包括控制依賴圖(Control Dependence Graph,CDG)和數(shù)據(jù)依賴圖(Data Dependence Graph,DDG),是源代碼的一種圖形表示,是帶有標(biāo)簽的有向圖,節(jié)點代表語句,邊表示兩種依賴關(guān)系。由于程序依賴圖(PDG)通過在節(jié)點標(biāo)簽中保留代碼信息,用圖的有向邊保留程序之間的上下文關(guān)系,可以更多地保留程序的控制依賴和數(shù)據(jù)依賴關(guān)系,因此該文選用了程序依賴圖(PDG)的表征方式。
在Sourcedg通過JAVA源代碼生成程序依賴圖(PDG)的過程中,為了更好地表達節(jié)點信息,去除冗余信息,保留上下文關(guān)系,將程序依賴圖的節(jié)點和邊劃分為多種類型,通過不同類型之間的關(guān)系進行圖的繪制,有選擇地保留節(jié)點和邊。Sourcedg生成程序依賴圖的過程中更多地關(guān)注于類型為類聲明、實參的傳入傳出、形參的傳入傳出、方法入口和賦值等操作節(jié)點,對于其他類型的節(jié)點則較多的省略。在程序依賴圖的邊中,分為實際控制邊和非實際控制邊,實際控制邊是指上下文中確實存在著控制關(guān)系,用實線邊表示;為了更好地表達數(shù)據(jù)流和控制流,PDG中會產(chǎn)生一些與源代碼無關(guān)的僅表示圖結(jié)構(gòu)的節(jié)點,在該類節(jié)點的關(guān)系中,這類則為非實際控制邊,常用虛線邊表示。
盡管Sourcedg工具生成的程序依賴圖已經(jīng)很大程度地保留了原代碼的有價值內(nèi)容,盡可能通過控制流和數(shù)據(jù)流表達了上下文關(guān)系。但當(dāng)源文件中存在調(diào)用其他文件的操作時,在源文件的程序依賴圖中并沒有任何表達,僅將該類調(diào)用語句作為一個普通節(jié)點,不考慮調(diào)用產(chǎn)生的數(shù)據(jù)流和控制流。對于跨文件的函數(shù)調(diào)用,Sourcedg無法發(fā)現(xiàn)文件之間及函數(shù)之間的關(guān)系,僅僅依靠PDG的表征方法對多文件的函數(shù)調(diào)用產(chǎn)生的漏洞檢測效率低下。
以圖2所示的代碼為例,源文件A調(diào)用BC源文件,ABC源文件在單個檢測時并無漏洞,但由于A調(diào)用了BC,導(dǎo)致在參數(shù)傳遞之后在A中形成了一個較為典型的SQL注入漏洞語句。在常見的漏洞檢測方法中,一般在文件粒度上進行檢測,即ABC分別檢測,A中調(diào)用語句產(chǎn)生的數(shù)據(jù)流和控制流并不會被過多關(guān)注,僅僅是作為普通的語句進行檢測。對于單個文件即造成漏洞的對于ABC該種文件間調(diào)用傳參而造成的代碼漏洞則被一定程度上忽略,可能變成漏洞攻擊的薄弱之處。因此,該文的主要研究目的是要捕獲ABC代碼塊之間的調(diào)用關(guān)系,通過觀察調(diào)用關(guān)系而產(chǎn)生的數(shù)據(jù)流和控制流進一步進行漏洞檢測。
圖2 代碼示例
在PDG的基礎(chǔ)上進行節(jié)點的遍歷,確定當(dāng)前文件是否調(diào)用了其他文件,若有調(diào)用,則將被調(diào)用文件劃分在當(dāng)前文件的代碼群中,存放在一個序列中。在構(gòu)建代碼群時的過程中,用字典的形式來存儲任一單文件內(nèi)的方法和聲明方法的節(jié)點地址,即字典名{方法名:節(jié)點地址}。在產(chǎn)生調(diào)用關(guān)系之后,查找被調(diào)用文件中該方法的節(jié)點地址,最后存儲為{調(diào)用節(jié)點地址:被調(diào)用地址}的格式,即為圖融合中需要新增的邊信息。在多文件中反復(fù)多次調(diào)用某一方法時,并不需要重復(fù)多次添加該方法相關(guān)節(jié)點,僅需添加相應(yīng)的邊來表達之間的調(diào)用關(guān)系。提取PDGcross特征的算法偽代碼如算法1-3所示。
算法1:PDGcross生成算法
輸入:輸入樣本的PDG特征集X={X1={nodes={n1,n2,…,nM},edges={e1,e2,…,eN}},…,Xq}
輸出:輸出樣本的PDGcross特征O={O1={nodes={N1,N2,…,Nk},edges={E1,E2,…,ET}},…,Oq}
1.union←[] //初始化代碼群列
2.entryDict←{}//初始化方法字典
3.dependeOther←{}//初始化依賴關(guān)系字典
4.forx←X1toXqdo//遍歷PDG特征集
5.list←[]
6.調(diào)用算法2生成entryDict
7.調(diào)用算法3生成list和dependeOther
8.union.append(list)
9.end for
10.for files[]in union do//將屬于同一代碼群中的文件融合
11.if files[]長度>1 then//說明該文件存在調(diào)用關(guān)系
12.for file in files[]do//遍歷文件代碼群
13.融合該代碼群中的PDG圖節(jié)點和邊
14.for nodes in dependeOther[file]do//遍歷節(jié)點之間的依賴關(guān)系
15.添加相應(yīng)的表示控制關(guān)系的邊
16. end for
17. end for
18.end if
19.存為Oi
20.end for
21.returnO//輸出結(jié)果
算法2:方法字典生成算法
輸入:輸入樣本的PDG特征X={nodes={n1,n2,…,nM},edges={e1,e2,…,eN}}
輸出:輸出方法字典entryOne={{labelMeans1:node1},{labelMeans2:node2},…,{labelMeansM:nodeM}}
1.entryOne←{}//初始化方法字典
2.for node←n1tonMdo//遍歷PDG的節(jié)點
3.labelType←node中的標(biāo)簽信息
4.labelMeans←node中的節(jié)點內(nèi)容//包含方法名
5.if(labelType的類別==‘Entry’)(labelMeans not in entryOne)then//該節(jié)點為方法節(jié)點且該方法為被加入方法字典
6. entryOne[labelMeans]←node//鍵值對為(方法名:節(jié)點地址)
7.end if
8.end for
9.entryDict[]=entryOne
算法3:依賴關(guān)系生成算法
輸入:輸入樣本的PDG特征X={nodes={n1,n2,…,nM},edges={e1,e2,…,eN}}
輸出:輸出依賴關(guān)系字典dependes
1.dependes←{}//初始化依賴關(guān)系字典
2.list.append(X)//把當(dāng)前文件添加至代碼群
3.for node←n1tonMdo//遍歷PDG的節(jié)點
4.labelType←node中的標(biāo)簽信息
5.labelMeans←node中的節(jié)點內(nèi)容//包含方法名
6.if labelMeans用正則表達式匹配到調(diào)用關(guān)系格式 then
7. filename←被調(diào)用文件名
8. method←被調(diào)用文件名
9. if filename是輸入文件之一 then
10. if filename not in list then
11. filename調(diào)用算法2和算法3
12. end if
13. if filename in entryDict then
//依賴關(guān)系字典當(dāng)前節(jié)點值列表中添加方法節(jié)點地址
14. dependes[node].append(entryDict[filename][method])
15. end if
16. end if
17.end if
18.end for
19.dependeOther[X]←dependes//賦值
在圖表征的基礎(chǔ)上,采用了圖嵌入算法將圖表示為低維、實值、稠密的向量形式,以數(shù)值化的方式表達圖中的信息,供給學(xué)習(xí)模型直接使用。圖嵌入的方法主要分為矩陣分解、隨機游走和深度學(xué)習(xí),圖基于隨機游走技術(shù)的頂點嵌入經(jīng)典算法包括DeepWalk、Node2Vec、SDNE等,該文則選取了其中的Node2Vec算法。Node2Vec算法在DeepWalk的基礎(chǔ)上改進了隨機游走的生成方式,采用有偏的隨機游走方式獲取頂點的近鄰序列,使得生成的隨機游走可以反映深度優(yōu)先和廣度優(yōu)先兩種采樣的特性,再利用word2vec去學(xué)習(xí)頂點的embedding向量,最后得到45*64的特征矩陣。
在PDGcross特征的基礎(chǔ)上通過Node2Vec圖嵌入得到特征矩陣后,按照文件是否含有漏洞為該文件對應(yīng)的特征矩陣添加相應(yīng)的標(biāo)簽,1為有漏洞,0為無漏洞,利用長短時記憶神經(jīng)網(wǎng)絡(luò)(LSTM)在含有標(biāo)簽的數(shù)據(jù)集上訓(xùn)練出分類模型,進行漏洞類別的預(yù)測。
在實驗中,首先以CWE89類漏洞進行模型訓(xùn)練。模型的訓(xùn)練集負(fù)樣本選擇Juliet數(shù)據(jù)集CWE89類別中單文件即構(gòu)成漏洞的數(shù)據(jù)集,包含352個樣例;正樣本隨機選擇benchmark數(shù)據(jù)集中無漏洞的350個樣例。有漏洞文件的特征矩陣標(biāo)簽為1,無漏洞的則為0,數(shù)據(jù)集的90%劃分為訓(xùn)練集,10%劃分為測試集?;贚STM神經(jīng)網(wǎng)絡(luò),通過批標(biāo)準(zhǔn)化進行歸一化處理,利用自適應(yīng)調(diào)整學(xué)習(xí)率,快速又精確地獲得最優(yōu)模型。最終模型的學(xué)習(xí)效果十分不錯,在測試集上的精確率和召回率都有很好的表現(xiàn),最優(yōu)時可以達到100%的準(zhǔn)確率,從另一方面也可以證明圖表征在模型檢測方面有較好的效果。
為了驗證所提特征提取的有效性,將其與原PDG圖表征及現(xiàn)有的開源工具檢測的結(jié)果進行對比實驗。選用的數(shù)據(jù)集來自于NIST參考數(shù)據(jù)集SARD中的Juliet測試數(shù)據(jù)集JAVA語言版和OWASP組織下的OWASP Benchmark項目中的數(shù)據(jù)集。Juliet數(shù)據(jù)集包含了上百種CWE相關(guān)漏洞代碼,Benchmark數(shù)據(jù)集包含了11類漏洞和無漏洞數(shù)據(jù),該文選取了Juliet數(shù)據(jù)集中的三個子集與Benchmark數(shù)據(jù)集作為實驗數(shù)據(jù)來源。
實驗中選取了Juliet數(shù)據(jù)集中的CWE 15(External_Control_of_System_or_Configuration_Setting)、CWE 89(SQL_Injection)、CWE 90(LDAP_Injection)三類漏洞代碼,見表1,benchmark數(shù)據(jù)集中無漏洞文件隨機選取680個。
在Juliet數(shù)據(jù)集中,名為“****a.java”與“****b.java”的文件劃分為一組,通過入口文件調(diào)用其他文件后組合產(chǎn)生一個漏洞,具有一個漏洞標(biāo)簽,稱這樣的一組為一個多文件樣例。在Juliet數(shù)據(jù)集的一個多文件樣例中,名為“****a.java”的文件為該組樣例的入口,稱其為該組樣例的入口文件。
將Juliet多文件樣例的入口文件又細(xì)分為AGS和G2BS兩類,AGS類為整組樣例中每單個文件并無漏洞,但因調(diào)用關(guān)系后產(chǎn)生漏洞,G2BS類為入口文件無漏洞但被調(diào)用文件有漏洞。實驗重點關(guān)注于AGS類數(shù)據(jù)集的漏洞檢測,針對G2BS類的數(shù)據(jù)可以作為安全風(fēng)險提示。以CWE89的多文件樣例為例,數(shù)據(jù)分布如表2所示。
表2 多文件樣例構(gòu)成
在人工檢驗后可以認(rèn)證該類多文件樣例確實包含相應(yīng)的漏洞,然而在使用第三方檢測工具Fortify和CodeSec掃描后,發(fā)現(xiàn)該類代碼在檢測工具中進行掃描時未發(fā)現(xiàn)該類漏洞,因此認(rèn)為進行跨文件的漏洞檢測十分具有必要性。
在實驗中,選取了precision、recall和F1值作為結(jié)果的衡量指標(biāo)。precision為精確率,表示正確預(yù)測漏洞種類的樣本數(shù)占全部預(yù)測為該類漏洞的樣本數(shù)的比例;recall為召回率,表示正確預(yù)測漏洞種類的樣本數(shù)占實際為該類漏洞的樣本數(shù)的比例;F1值為和的調(diào)和平均數(shù)。
(1)
(2)
(3)
其中,TP表示預(yù)測為某類漏洞且分類準(zhǔn)確的樣本數(shù)量,FP為預(yù)測為某類漏洞但實際可能不存在漏洞或為其他漏洞的樣本數(shù)量,FN為實際為某類漏洞但未被正確檢測的樣本數(shù)量。
首先選取CWE89漏洞數(shù)據(jù)與benchmark無漏洞數(shù)據(jù)訓(xùn)練二分類模型,在訓(xùn)練得到CWE89漏洞的二分類模型后,抽取CWE89數(shù)據(jù)集中多文件樣例的119組AGS類樣例的入口文件和92組G2BS類樣例的入口文件及單文件樣例作為測試集進行漏洞分類。在實驗中,特征提取階段分別使用Fortify檢測工具與PDG和PDGcross表征進行對比,檢測結(jié)果如表3所示。
表3 模型檢測結(jié)果
由實驗可知,Fortify和PDG特征目前僅在單文件漏洞方面有不錯的效果,但在多文件產(chǎn)生組合漏洞的樣例檢測中漏報率較高。而所提出的特征不論是在檢測單文件漏洞、無漏洞文件因調(diào)用而產(chǎn)生漏洞(AGS類)的情況還是警告某一無漏洞文件調(diào)用有漏洞文件(G2BS類)的風(fēng)險時都有較為不錯的效果。因此在實驗中進一步添加了CWE15和CWE90兩類漏洞,訓(xùn)練了一個四分類模型,進行多文件樣的檢測,取得了91%的精確率和90%的召回率,優(yōu)于現(xiàn)有的開源工具檢測方法,結(jié)果如表4所示。
表4 四分類模型檢測結(jié)果
聚焦于多文件間調(diào)用產(chǎn)生的漏洞檢測,確定了被調(diào)用的文件范圍,利用圖表征技術(shù)中的程序依賴圖實現(xiàn)了多文件的融合。采用LSTM神經(jīng)網(wǎng)絡(luò),利用批標(biāo)準(zhǔn)化進行歸一化處理,學(xué)習(xí)且訓(xùn)練出相應(yīng)的漏洞分類模型。實驗在CWE15、CWE89、CWE90這三種漏洞的小規(guī)模數(shù)據(jù)集上取得了91%的精確率和90%的召回率,在檢測多文件調(diào)用漏洞方面更是優(yōu)于CodeSec和Fortify等工具。但是目前漏洞檢測工作是針對于文件粒度,缺少函數(shù)粒度的標(biāo)簽,無法更進一步的確定漏洞范圍,因此下一步的工作是將代碼粒度細(xì)化,減少特征中的冗余信息,做到一定程度的漏洞定位。