李帥剛,王全民
(北京工業大學 信息學部,北京 100124)
隨著我國互聯網的突飛猛進,Web 應用在不同場景和業務中變得越來越重要,這也給不法分子提供了數不勝數的Web 漏洞靶場.作為一種常用的攻擊工具,Webshell是以asp、php、jsp 等常見的網頁文件形式存在的一種代碼執行環境,也被叫做網頁后門.攻擊者通過層出不窮的Web 漏洞進入到后臺管理系統后,通常會將php 或者jsp 后門文件與網站服務器Web 目錄下正常的網頁文件混在一起,然后就可以使用瀏覽器來訪問Webshell,從而進行文件上傳下載或者進一步的權限提升.根據國家互聯網應急響應中心《2019年上半年我國互聯網網絡安全態勢報告》,CNCERT 檢測發現境內外約1.4 萬個IP 地址對我國境內約2.6 萬個網站植入后門,同比增長約1.2 倍,其中,約有1.3 萬個(占全部 IP 地址總數的91.2%)境外IP 地址對境內約2.3 萬個網站植入后門.同時,發生在我國云平臺上的網絡安全事件或威脅情況相比2018年進一步加劇,被植入后門鏈接數量占境內全部被植入后門鏈接數量的6 成以上[1].當前檢測Webshell的方法如靜態規則匹配、日志文件分析以及機器學習分類等,在抵抗靈活多變的逃逸技術時[2],未能有效保護服務器的安全.針對現狀,急需一種能夠有效檢測Webshell的方法.
與正常網頁文本的區別在于,Webshell 存在大量系統調用和文件操作函數如eval、system、cmd_shell、readdir 等,攻擊者可通過上述特征函數遠程操作服務器.因此,可以從文本分類的角度出發識別Webshell.隨著深度學習的不斷發展,多種算法模型在文本分類中表現優異.本文使用不同的算法模型進行實驗對比,結果表明,Bi-GRU的檢測效果最佳,能夠有效解決當前檢測Webshell 存在的問題.
近年來,對Webshell 檢測方法主要分為3 類,分別是基于流量的檢測、基于日志的檢測和基于文本的檢測.
基于流量的檢測是在網頁文本與服務器通信過程中,通過分析http 請求與響應數據來識別Webshell.王應軍[3]從Webshell 客戶端工具和Webshell 執行原理的角度出發,對該過程中通信特征進行分析.Starov[4]提出不同類型Webshell 存在某些共性特征,將這些特征作為分類的依據.基于流量的檢測,雖然能夠達到一定的準確率,但是受限于人工觀察樣本和復雜的匹配規則.
基于日志的檢測通過分析應用程序執行后生成的日志文件,找到異常的請求日志,從而識別出Webshell.石劉洋等[5]通過文本特征匹配,分析文件關聯性進行檢測,研究總結出Webshell 不常規行為生成的日志與正常日志存在巨大的出入.潘杰[6]采取文本分類SVM對日志進行檢測,首先將日志進行聚類,然后對結果進行分析.但是,基于日志的檢測方法存在滯后性,即攻擊者攻擊了系統之后,才會被發現.
基于文本的檢測的優勢在于發現及時、特征分析方便.孟正等[7]提出了一種基于SVM的檢測方法,該方法的準確率為99%,存在的不足之處是惡意樣本較少.張涵等[8]提出一種基于多層神經網絡的方式來進行檢測,將代碼序列通過嵌入層轉化為向量,然后使用神經網絡進行檢測.姜天[9]采取卷積神經網絡模型檢測Webshell,該方法整體效果不錯,但評估方法中缺少漏報率等硬性評價指標.周龍等[10]提出了一種基于循環神經網絡的方法來檢測Webshell,使用TF-IDF 獲取特征,以GRU為樣本訓練模型,不過其準確率還有待提高.
目前,在Webshell 檢測的學術研究中常用的算法有SVM、MLP、CNN、RNN 等,這些算法模型有一定的識別Webshell 能力,但還存在準確率較低,誤報率和漏報率較高等缺點.
考慮到php、asp、jsp 等文本形式的Webshell 在語句邏輯上相差不大,且asp和jsp 在當前Web 開發中逐漸被淘汰,因此本文選擇php 網頁腳本作為研究對象.在特征選擇方面,使用Word2Vec 算法[11]將詞語轉換為特征向量,該算法能夠表征詞語之間的關聯度.在算法模型選擇方面,循環神經網絡[12]在訓練序列數據時表現優異.本文最終選擇能夠傳遞正向和反向語句信息的Bi-GRU 作為分類算法,從前后兩個方向挖掘語句信息,以提高檢測效果.具體的研究過程如圖1.

圖1 研究過程
數據的預處理工作主要包括文件去重和編譯獲取opcode 指令[13].文件去重的目的是防止數據中出現大量相同的樣本,主要是通過計算每個樣本的MD5 值,進行比對,然后剔除相同的樣本,以防止污染訓練數據.完成文件去重之后,需要將php 源碼轉換為opcode 指令.opcode是php 源碼編譯之后生成的中間代碼,它將php 腳本中可執行語句轉換為Zend 支持的135 條指令.php 文件在服務器中的執行流程可分為兩步:第1 步啟動Zend 引擎,加載開啟的擴展模塊;第2 步Zend 引擎對php 文件進行一系列操作后得到opcode,然后執行opcode.只要源碼中實現Webshell 相關功能,就會在opcode 指令中體現出來.在編譯opcode的返回信息中,需要通過正則表達式獲取opcode 指令字符串.以最常見的一句話木馬webshell為例.

代碼1.一句話木馬Webshell Php@eval($_POST[‘cmd’]);?>
其原理是通過post 方式傳入一個可執行的方法,然后eval 函數執行該方法,服務器將運行結果通過http 返回給用戶,這樣就如同拿到管理員權限一樣,可以直接對服務器進行操作.通常一句話木馬需要配合一些工具來使用.對一句話木馬進行編譯得到的opcode指令如序列1.

序列1.opcode 指令BEGIN_SILENCE FETCH_R FETCH_DIM_R INCLUDE_OR_EVAL END_SILENCE RETURN
通過預處理后得到opcode 指令字符串,不能被算法模型識別.因此,需要提取樣本的特征向量,以數字的形式輸入到算法模型中.本文使用Word2Vec 獲取文本特征向量.
Word2Vec 在自然語言處理中被大量的使用,它通過無監督的方式從語料庫中學習詞語之間的相似性,然后將每個詞用一個向量來表示,從而用向量表征詞的語義信息.最早的文本向量化技術如onehot 編碼,通過建立一個語料庫大小的一維向量,只是簡單的將單詞所在的位置設為1,其他位置設為0.通過上述方式得到的向量存在維度災難、無法表征詞語之間的語義信息等問題.而上述問題,在Word2Vec中可以被很好的解決.Word2Vec 模型結構有兩種,一個是CBOW(Continuous Bag Of Words)模型,通過給定上下文來預測輸入的值;另一個是Skip-Gram,通過給定輸入的詞,來預測上下文.后者的結構如圖2所示,輸入層用onehot 編碼的向量,輸出層通過一個Softmax 函數輸出0-1 之間的值,表示當前詞語與輸入詞之間的相似性,所有輸出層的值和為1.在隱層中定義詞語需要轉換的維度,該層中的權重矩陣是網絡學習的最終目標.本文選擇Skip-Gram.

圖2 Skip-Gram 結構[11]
本文提出一種基于深度學習的Webshell 檢測算法模型,該模型使用GRU 作為神經網絡結構.GRU(Gated Recurrent Unit)[14]是LSTM (Long Short-Term Memory)的簡化變體,其結構如圖3所示.它將LSTM最初的3個門,輸入門、輸出門和遺忘門合并為重置門和更新門.這樣,即可以解決RNN 在訓練過程中的梯度消失和梯度爆炸的問題,同時又能使計算開銷減小.GRU的輸入輸出結構與普通的RNN 一致,存在一個xt和上個節點傳遞下來的隱狀態(hidden state)ht?1,該參數保留了前一序列狀態的信息,同時也作為GRU的輸入的值.通過當前節點的輸入xt與上一節點的保留信息ht?1計算兩個門控制狀態.

圖3 GRU 結構圖[14]
圖3中,zt為更新門(update gate),定義了前面記憶保存到當前時間步的多少,計算過程如式(1)所示,其中Wz表示權重,使用σ函數將結果控制在0~1 之間,結果值越大,保留之前的信息就越多.重置門(reset gate)決定了對之前序列信息的遺忘程度,公式如式(2)所示,整個步驟與更新門一致.h′t用來表示當前序列記憶內容,計算過程如式(3)所示,重置門起到遺忘多少保留信息的作用,使用tanh 作為激活函數.ht的作用是保留當前單元的信息并傳遞到下一個單元中,使用更新門來決定當前記憶內容和上一個序列中需要保留的信息,其計算公式如式(4)所示.


本文實驗框架如圖4所示.樣本通過嵌入層將每個opcode 指令轉換為特征向量,接著輸入到Bi-GRU中,將正向通過GRU輸出的隱狀態與反向通過GRU輸出的隱狀態拼接,從正向和反向獲取樣本中上下文信息.值得一提的是,本文選擇將雙向GRU的輸出值作為全連接層的輸入,該層輸出一維向量,然后使用Softmax函數計算最終的概率,如式(5)所示.將模型輸出值與0.5 進行比較,如果大于0.5,說明為正常樣本,否則為Webshell[15–17].

圖4 實驗框架

實驗數據分為Webshell 樣本和正常網頁文本.其中Webshell 樣本來源于Github 排名靠前的倉庫如phpwebshells、webshellSample 等.正常樣本來源于,當前用戶量較大的php 框架如Thinkphp、WordPress 等.其中部分數據來源如表1所示.由于不同Webshell 所使用的php 版本存在不一致的情況,這造成部分樣本編譯opcode 失敗,因此在編譯opcode 指令過程中直接忽略報錯的樣本.為了方便進行后續分類,本文將正常樣本的標簽設置為1,而Webshell 樣本設置0.

表1 數據來源
對原始樣本進行編譯后,最終得到5830 樣本,其中正常腳本個數為4664,Webshell 腳本個數為1166,兩者比例約為4:1.
系統結構如圖4所示,每個樣本中opcode 指令的個數為400,超出的部分直接劃掉,不足的樣本用0 填補.通過Word2Vec 訓練后,opcode 指令轉換為特征向量,維度為128,將其輸入到Bi-GRU中進行訓練.在GRU 層中,正向和反向中GRU 單元的個數都為128,選擇dropout為0.2 防止過擬合的產生.輸出層中的激活函數為Softmax.
實驗采用Adam 優化算法,學習率為0.01,損失函數使用交叉熵.其中訓練集和驗證集的比列為8:2,共計訓練5個epoch.
本實驗是一個二分類問題,選擇準確率、假正率和假負率作為模型評估的標準.Webshell 樣本可看作負樣本,正常樣本為正樣本.根據表2定義混淆矩陣.

表2 混淆矩陣
準確率(Accuracy,Acc),表示預測結果正確的比率,計算過程如式(6)所示.

假正率(False Positive Rate,FPR),表示Webshell被預測為正常樣本的比率,可作為漏報率,計算過程如式(7)所示.

假負率(False Negative Rate,FNR),表示正常樣本被預測為Webshell的比率,可作為誤報率,計算過程如式(8)所示.

為了驗證Bi-GRU的有效性,本文共使用了7 種算法進行對比分析.在SVM和MLP的訓練過程中,特征向量皆為原始樣本的opcode 指令.卷積神經網絡算法使用TextCNN[18]的模型結構,將單個文本轉換為二維向量,建立卷積層,從圖片角度對文本進行分類.在循環神經網絡的算法模型中,分別比較了LSTM、Bi-LSTM和GRU 模型的分類效果.最終實驗數據如表3所示.

表3 實驗數據
從表3中可以看出,Bi-GRU 算法明顯優于其他算法.Bi-GRU 在文本分類中的準確率最高,達到了98%,高于GRU 約1.2%,是因為Bi-GRU 獲取的是文本中雙向記憶信息,結構更復雜.同時,LSTM和Bi-LSTM在識別Webshell的準確率上表現差勁,分別為93%和89%,其他3 類算法中CNN的準確率表現較好,但準確率低于Bi-GRU 約1.1%,說明CNN 并不適用于Webshell的檢測.在7 種算法模型中,Bi-GRU的漏報率最低,為3.5%左右,但是誤報率稍遜于SVM和CNN,但在真實的檢測環境下,更應該關注其漏報的情況.
圖5是所有分類算法訓練結果計算出的ROC 曲線,其中Bi-GRU、GRU、CNN 更加靠近左上角,說明分類效果遠超其他4 類算法.Bi-GRU 將樣本的中的每個opcode 當做序列,前面時間步的信息會被保留下來,作為本時間步喂養的數據.在模型訓練的過程中,學習Webshell 上下文中特殊的函數組合與語句邏輯,將學習到的內容作為識別Webshell和正常樣本的依據.同時,從正反兩個方向中發現語句的關聯信息,對Webshell的識別也起到了很大的幫助.

圖5 ROC 曲線
本文提出了一種基于Bi-GRU的Webshell 檢測方法,以php的opcode 作為原始數據,設定樣本中opcode指令的個數為400,使用Word2Vec 獲取特征向量,采用七種不同的算法模型進行對比試驗,得出Bi-GRU的模型在檢測Webshell的準確率、漏報率上明顯優于其他算法模型,但是在誤報率上還存在不足.
在接下來的研究中,考慮使用詞袋模型對樣本中的opcode 指令進行切分組合,結合TF-IDF 獲取詞頻特征,嘗試其他文本分類模型如圖卷積神經網絡,解決誤報率高的問題,進一步提高模型檢測Webshell的效果.