陳鈞衍 陶非凡 張 源
1(復旦大學軟件學院 上海 201203)2(上海通用識別技術研究所 上海 201114)
互聯網的影響滲入現代生活的方方面面,運行在個人電腦與智能移動設備上的各類軟件在其中扮演了重要的角色。這些軟件在為現代生活提供了各種便利的同時,也成為了惡意攻擊者的跳板。軟件開發過程中的錯誤實現會導致安全漏洞,攻擊者可以利用它們通過互聯網竊取或篡改受害者的設備上的數據。
知名軟件當中的已知安全漏洞在發布時會得到一個通用漏洞披露(Common Vulnerabilities and Exposures,CVE)號作為統一的稱呼和引用方式。CVE數據庫中除了CVE號和漏洞本身的描述,還包含漏洞的嚴重程度、攻擊類型、目標平臺、軟件版本和攻擊方式等信息。盡管其攻擊原理已經被公布,但不夠謹慎的開發者未必能及時為軟件與設備部署安全補丁。與此同時,攻擊者也常常利用已知漏洞的攻擊原理來攻擊新的軟件與系統。為了深入理解已知漏洞的工作機制與原理,研究者常需要對它們進行分析與測試。為此,研究者首先需要在CVE數據庫中篩選出滿足條件的漏洞。其篩選條件往往是受影響軟件、攻擊效果等關鍵信息。在確定了待研究漏洞之后,還需要分析其主要特征,得到受影響軟件、攻擊方式、漏洞觸發條件等信息。這樣才能正確地測試與利用漏洞。
與研究者的需求相悖的是,現有的CVE數據庫相當不完善。一個CVE的核心是它的描述文本,其關鍵信息以非結構化的形式包含其中,研究者無法簡單地利用。此外,雖然目前的CVE數據庫中已經附帶了幾項結構化的輔助信息,如CPE、CVSS等,然而有調研[2]指出:已有的輔助信息無法覆蓋到所有CVE,且內容與形式也缺乏一定之規,甚至常有錯漏。因此已有的輔助信息亦無法作為足夠可靠的信息來源。
在CVE的數量有限時,安全研究者尚可人工篩查數據庫給出的結果。然而隨著互聯網應用和用戶數量的快速增長,網絡安全問題受到了前所未有的重視。如圖 1所示,從2017年開始,每年新增的CVE數量超過一萬個,出現了一波爆發式的增長。效率較低的手工分析與不夠精準的索引已經無法滿足安全研究的需求。為了更好地分析研究CVE,本文將描述文本轉換成結構化的形式,研究者能夠憑借結構化的信息更好地分析與索引CVE數據。

圖1 歷年CVE數量變化趨勢
本文使用信息抽取技術從CVE的漏洞描述文本當中抽取結構化的信息。為此,本文首先對已有的大量CVE描述文本進行了研究,總結出漏洞描述的七個核心要素。之后,為了自動化地從描述文本當中提取出漏洞要素,本文引入了自然語言處理的技術。然而此類描述文本往往包含大量的安全領域專有詞匯,已有的成熟句法處理模型并不能在漏洞信息抽取問題上給出優質的分析結果。因此本文選擇引入機器學習的方法,訓練領域專用的詞向量和信息抽取模型。
在考察了機器學習與自然語言處理領域的新興成果之后[6,8],本文將信息提取的問題轉換為一個序列標注模型:將漏洞描述的文本視為一個序列,以單詞為最小輸入單元。而模型為每一個輸入的單詞輸出一個標簽,這個標簽說明了該單詞所屬的漏洞要素。本文設計神經網絡并在數據集上訓練整個標注過程,最終實現對漏洞要素信息的結構化提取。
本文的主要貢獻有:
(1) 提出了漏洞信息的結構化表示方案,以統一的形式對漏洞進行描述。
(2) 設計了基于序列標注的方案,解決了從非結構化的CVE描述文本中抽取結構化信息的問題。
(3) 構造數據集,訓練模型實現上述方案,并設計實驗驗證了該模型的信息抽取效果。
目前并沒有直接從CVE當中抽取結構化的信息的工作。部分工作與本文的目標近似,試圖補充CVE的結構化信息。而另一些工作與我們一樣,在安全領域的文本上應用了機器學習與自然語言處理的技術。本文將對幾種不同類型的工作分別進行講解。
部分研究者試圖從CVE構建攻擊圖(Attack Graph)[1],以此輔助安全系統的分析與建模。文中指出,已有的CVE數據庫并不能提供形式統一的結構化信息。因此他們嘗試從CVE的文本描述中提取信息,并以此完成攻擊圖的構建。但其信息提取策略仍然依賴于已有的CVSS、CPE、OVAL等輔助信息,而沒有充分利用非結構化的描述文本。文獻[3]則將機器學習技術引入到CVE文本信息提取的領域,使用卷積神經網絡從CVE文本直接生成CVSS數據。然而CVSS的格式仍然較為簡單,其使用的技術不足以支持進一步的復雜結構化信息提取。
部分威脅情報的工作[4-5]使用已有的成熟NLP句法依賴模型[10]處理安全文本。他們嘗試從威脅指標(Indicators of Compromise,IoC)與網絡威脅情報(Cyber Threat Intelligence,CTI)的自然語言文本當中提取信息。而Semfuzz[7]也使用了相似的處理手段,其分析目標是CVE的描述文本。但他們的技術仍然受限于已有的通用模型,在遇見安全領域的專有名詞時,可能給出錯誤的解析結果。Igen[9]放棄了通用語言模型,轉而使用卷積神經網絡來從IoC中提取域名、IP一類的信息。不過它處理的仍然是特征較為明顯且形式較為單一的內容,無法適用于本文所面臨的問題。
現有工作所提出的方案都存在各自的缺陷,接下來本文將闡述所設計的方案如何解決這些問題。
對CVE和漏洞本身的深入理解是結構化信息抽取方案的基礎。本節將闡述對漏洞進行完備描述所必需的核心要素,以此為后續提供一個結構化的抽取模型。為此,本文調研了1999年-2017年間產生的七萬余CVE,并對各類漏洞描述模式進行了總結,以覆蓋所有可能情形。
對一個漏洞而言,最重要的信息是它所影響的軟件與平臺,以及它影響該軟件的方式。漏洞影響軟件的方式可以細分為漏洞類型(例如:SQL注入,緩沖區溢出)、導致漏洞產生的實現錯誤原因、漏洞觸發環境(特定的配置或運行方式)、攻擊要求(是否需要本地接入,是否需要認證)、攻擊途徑(例如:通過上傳文件,通過定制參數)以及攻擊所造成的結果。一個CVE的漏洞描述文本不一定會包含所有要素,但其中表述的重要信息總能被歸于這些要素之一。
圖2以兩個真實的CVE文本為樣例,說明七個漏洞要素的內容。本文將在此結構化方案的基礎上繼續闡述后續的信息抽取方案。

圖2 漏洞結構化模型示例
從CVE的描述文本當中提取結構化信息并不容易,本文面臨著若干挑戰:
首先,CVE的描述文本以自然語言的形式存在,沒有嚴格的書寫規范。不同軟件、不同時期或不同作者撰寫的描述文本風格之間差異較大,且包含的漏洞要素種類也不一致。因而漏洞文本中的結構化信息無法簡單地以固定的模板抽取。
已有工作[4-5,7]中引入了自然語言處理技術來提取安全領域的文本中的信息,然而它們僅憑借已有的通用NLP模型處理文本。無論是基于貝葉斯方法亦或者神經網絡,這些模型都是從大量的普通文本訓練而來,對安全領域專有名詞處理能力較差。舉例而言,一個漏洞的描述當中必然包含受影響的軟件名稱, CVE的文本中因而存在大量的軟件名。當它們是一個不常見的單詞(例如:Apache)時,已有模型尚且能夠將其識別為一個專有名詞;但當它們本身就是常用詞(例如:ruby,curl)時,模型便會給出完全錯誤的解析結果。除此之外,描述漏洞時還常常使用一些安全領域的專有詞匯(例如:stack, overflow, exploit),這些詞的用法和詞性與日常不同,常常會將模型的解析結果導向錯誤的方向。這就使得通用模型并不能在漏洞描述文本上給出較好的解析結果。
針對這些挑戰,本文提出了自己的解決方案:引入機器學習的方法,在標注好的漏洞描述文本數據集的基礎上訓練領域專用的模型,并用此模型來完成文本信息抽取的任務。
本文并非首個利用機器學習技術解析CVE描述文本的工作。曾有工作[3]嘗試通過卷積神經網絡來完成從文本到漏洞嚴重等級的映射。然而信息抽取這項任務上所需的輸出并非一組簡單的浮點數字,而是源文本當中的幾個有意義的字符串,以及它們所對應的要素類別。這對模型的設計提出了更高的要求。為此,本文考察了自然語言處理領域的分詞和實體名詞識別等工作的研究成果,決定將信息抽取問題轉換為一個序列標注問題。此模型接受一個文本序列作為輸入,序列中的每一個元素都是一個單詞。每輸入一個單詞,該模型都會為其給出一個標注值作為輸出。以CVE-2016-7103為例,其描述文本中每個單詞的標注結果如圖 3所示。每一個單詞得到了0~7之中的一個標注值。其中0為無用成分,而1~7分別對應漏洞描述中七種不同的要素。“Cross-site scripting XSS vulnerability”短語中的四個單詞均被標注為“1”,說明模型將該短語判斷為此CVE的漏洞類型。

圖3 序列標注示例
在討論模型的架構細節之前,本文將先對輸入數據格式轉換的問題進行說明。大多數模型只能接收向量形式的輸入格式,無法直接對字符串進行處理。因此,在使用模型對文本數據進行標注之前,首先需要將每個單詞轉換成對應的詞向量。生成的詞向量必須滿足兩個要求:具備較低維數,以避免拖慢計算效率;能夠較好地在低維空間中體現出語義信息。
一種常見方式是將每個字符轉換為one-hot向量,即一個長度等于字典大小,且除目標字符對應位置為1外,其他位置全為0的向量。這種編碼方式的實現較為簡單,然而缺陷較大,通常無法在訓練中獲得比較好的效果。首先是計算效率上的考慮,此方式生成的詞向量過于稀疏,信息密度極低。且由于本文的模型中,字典大小等于所有可能出現的單詞數量,生成的詞向量長度過大,會嚴重拖慢計算速度。與此同時,不同的one-hot詞向量之間均為正交關系,無法體現出字面不同但含義相近的詞向量之間的聯系。
為滿足上述要求,本文采用了word2vec詞嵌入模型[15]來將每個單詞轉換為詞向量。該算法用語料庫訓練了一個能對上下文中的某個單詞做出預測的神經網絡。而訓練完成的網絡的隱藏層便是每個單詞所對應的詞向量。該算法能夠在保留主體信息的同時將單詞嵌入到低維空間,生成較小的詞向量。同時對于語義相近的單詞,其生成的詞向量在空間中也會具有較近的距離。這便能充分反映單詞之間的語義關聯。而為了讓所得詞向量充分體現該單詞在漏洞描述文本中的語義,本文以CVE的描述文本作為語料庫來訓練word2vec模型。
為序列(文本)當中的每一項元素(單詞)給出一個輸出并不簡單。與為每一個單詞給出一個特定的輸出不同,單詞的詞性和含義與上下文單詞相關,對一個單詞的標注并非孤立的過程。每個標注結果都必須依賴其上下文的內容,同時也要考慮其上下文中其他單詞的標注結果。
聯系上下文給出輸出的一個可行選擇是:帶有記憶單元的循環神經網絡。然而普通的循環神經網絡無法很好地處理長期依賴問題。它能夠正確處理序列中鄰近的上下文,但卻會在多次迭代之后稀釋遠距離信息。而在漏洞文本當中,上下文關聯并非是局部的,例如句末的代詞便可能直接與句首的主語相關。對此,缺乏足夠區分度的循環神經網絡無法很好地處理。
為了獲得更好的序列標注效果,本文選擇了LSTM(長短期記憶)網絡[11]。相較普通的循環神經網絡,LSTM網絡額外包含了輸入、輸出與遺忘的門限控制單元,能夠在訓練中獲得篩選重要數據的能力。經過合理訓練的LSTM網絡能夠充分利用遠距離的上下文的信息。另外,一個LSTM網絡僅能將序列前部的信息單向向后傳遞。但在文本當中,后文的內容也會影響對前文的理解,單向的記憶不足以對此做出處理。為此,本文添加了一個結構相同、但接受相反方向輸入的LSTM單元,共同構成雙向的LSTM網絡。這個雙向的網絡能綜合上下文的信息,為序列中每個元素給出更準確的標注結果。
在確定了基本的網絡結構后出現了另一個問題:當前的模型并未約束輸出格式,也沒有使用模型對標注序列本身的特征進行學習。因此,盡管神經網絡在訓練的過程當中確實能夠學習到原始文本的特征,卻時常給出對人類而言并不合理、甚至并不合法的標注結果。為了讓模型學習到輸出格式的分布特征,本文在雙向LSTM之后添加了一層CRF[12]單元。CRF是一個基于條件概率的模型,它能夠在訓練過程中學習到訓練集的標注結果本身的分布特點。CRF單元會在雙向LSTM給出的結果基礎上,推導出一個擁有極大似然概率的序列標注結果。
綜合上述的內容構建而成的網絡如圖 4所示。

圖4 LSTM+CRF神經網絡結構
在討論實驗過程之前,本文將先對各種數據的來源進行闡述。訓練上述的神經網絡模型需要一批已經經過標注的數據。然而按照前文所述,為標注一條數據,標注者需要在漏洞描述文本中找出可能存在的要素,并為每個單詞逐一標注對應的序號。這種標注方法較為復雜,要求標注者具備漏洞安全領域的知識,難度較大,且耗時較長。為了快速獲得大量的已標定數據,本文采用了人工標注與自動生成相結合的方式。
首先,兩個安全專家手工為隨機選擇的634個CVE的文本進行完整的標注,這些文本構成了人工標注數據集。這些數據隨后被平分成兩部分:一半作為檢測模型效果的測試數據集,而另一半則成為生成訓練數據的原型數據集。原型數據集的規模較小,并不能直接用于神經網絡的訓練。因此,本文復用了已標注文本當中的漏洞要素,人工添加部分內容,按照一系列的模板生成了訓練所用的數據集。
具體而言,本文根據用于訓練的已標注文本,為漏洞結構化描述中的每個要素都生成了一個可選列表,并對此列表加以人工拓展。列表當中的每個要素都已經過人工標注。之后,本文根據已有的CVE,制定了30種不同的描述文本模板,如圖5所示。

圖5 模板生成示例
一個模板中有兩個關鍵內容:待生成的描述文本的基本句式,以及其中出現的漏洞要素種類。模板當中,用雙花括號包圍的便是待填充的要素。數據生成時從對應類型可選要素列表中各任取一項,并整體替換對應要素。另外,為了保證生成的文本的多樣性,模板中的介詞和連詞(用雙方括號包圍)也會進行隨機化處理。本文針對不同句式分別設計了可選填充短語列表,在漏洞要素確定后,使用隨機選取的詞匯填充雙方括號部分。這樣就完成了文本生成的過程,本文按上述流程生成了總共十萬條已標注的描述文本數據。這些數據構成了模板數據集。
使用word2vec將輸入文本轉換為詞向量,需要先對其進行預處理。預處理的目的是去除文本當中無特定含義,不影響實際的標注結果的功能性內容,并對一些不合法的輸入內容做轉換。
首先是各類停止詞,例如:“the”,“is”,“at”,“which”。它們在文本中較為普遍,且通常不包含實際的含義。另外是各類標點符號,包括逗號、括號、引號等。它們會被對應的短語替換(如用LRB替換左括號,DQM替換引號)。最后一類是數字和符號,在CVE的描述文本當中常見如“3.1.3”或“4.0.1a”一類由數字字母和連接符號構成的版本號。它們的具體內容與標注結果并無關聯,因此本文對其進行了統一的替換,全部用“UNKN”代替。完成預處理之后,本文使用Gensim[13],通過word2vec詞嵌入算法將所有單詞轉換為300維的詞向量。
神經網絡使用Tensorflow[14]構建,該工具直接提供了封裝好的LSTM和CRF單元以供使用。本文在網絡中設置了3層雙向、總計6個LSTM單元,其隱藏層維數均為300。其中每個LSTM單元之后都附帶一層設置為0.1的dropout(丟棄層),以防止過擬合。三層雙向LSTM之后,是一層學習標注特征的CRF單元。而前面的輸出最終會通過一個邏輯斯蒂函數轉換為最終的標注結果。本文在訓練數據集上,使用交叉熵算法作為損失函數,以Adam優化算法和128的batch大小,對模型進行1 000輪訓練,得到了最終的訓練結果。
上一節敘述了如何在包含十萬條漏洞描述文本的模板數據集上完成對模型的訓練。本節將在人工標注數據集的另一半,也就是測試數據集上驗證其信息抽取的效果。
在展示結果之前,本文需先對結果評估的策略進行說明。訓練過程中使用較為簡單的方式計算損失函數:對比模型和測試集對每一個單詞的標注結果,并據此計算交叉熵。盡管在此評估策略下,模型能給出95%以上的準確率,但該結果并不能完全肯定模型的效果。本文要達成的目標是信息抽取,而非序列標注本身,每一個單詞的標注錯誤都可能導致整個漏洞要素含義不明。因此,本文在對模型的信息抽取效果進行自動化評估之時,僅當一個漏洞要素當中的每個單詞均標注正確時,才認為得到了一個正確的結果。
然而,嚴格的策略會錯過一些可能的正確結果。因為待提取的要素邊界有時是模棱兩可的。額外包含的謂語、介詞或者補充說明并不會妨礙人們對這些短語的準確理解。為此,除按照嚴格策略下的自動化評估之外,本文還人工核查了每一項信息抽取的結果。實驗結果如表 1所示。

表1 CVE文本信息抽取結果
從表中可以看出,嚴格策略和人工判定下準確率均較高,且人工判定總是會給出更高的準確率。根據觀察,這種偏差主要有以下兩類情況: (1) 存在多余的不影響短語理解的介詞和謂詞短語;(2) 部分漏洞要素包含冗余的補充解釋。總體而言,這兩類情況都不會阻礙對抽取出的漏洞要素的理解。因此,本文認為訓練所得的模型確實能夠以較高的準確率完成漏洞要素的信息抽取任務。
本文針對從CVE漏洞描述中抽取結構化信息的問題,分析了面臨的技術挑戰,提出一個通用的漏洞信息結構化表征方案。為實現信息的結構化抽取,本文將此問題轉換為一個序列標注模型,通過設計雙向LSTM和CRF的多層神經網絡模型實現了對CVE描述文本的結構化信息抽取。本文構建了大規模數據集來完成該神經網絡的訓練,并在人工標注的數據集上驗證了其效果,結果表明本文提出的方案可以實現較好的漏洞要素抽取效果。本文的工作實現了將CVE漏洞描述文本進行結構化抽取的第一步。