主機安全——洋蔥Webshell檢測實踐與思考


【騰訊安全平台部數據安全團隊】

前言

Webshell是網站入侵的常用后門,利用Webshell可以在Web服務器上執行系統命令、竊取數據等惡意操作,危害極大。Webshell因其隱秘性、基於腳本、靈活便捷、功能強大等特點,廣受黑客們的喜愛,因此Webshell的檢測也成為企業安全防御的重點,Webshell檢測已是主機安全系統的標配功能。洋蔥系統是騰訊自研的主機安全系統,Webshell檢測是其基礎功能之一,洋蔥系統在2008年上線了第一代Webshell檢測引擎,14年上線了動態檢測功能(RASP)並在TSRC進行了眾測,其后還增加了統計分析、機器學習等能力。2012年phpmyadmin某個分發節點被植入后門(CVE-2012-5159)就是被Webshell檢測引擎所發現。

近幾年攻防對抗不斷升級,防御的挑戰越來越大,並且大型互聯網公司服務器多、流量大,除了檢測能力,也要考慮業務實施成本、運營等多個方面,文件檢測、動態檢測、機器學習等單一方法都存在一些問題。基於以上原因我們提出了一種新的檢測思路,5月份拿PHP引擎在TSRC做了一次眾測,從測試結果看檢測能力較之前有很大提升,但也存在一些問題,本文主要是一些實踐思路和原理介紹,借助TSRC博客和大家做一個交流分享。

主流Webshell檢測思路

當前Webshell的檢測方式多以特征檢測、機器學習、動態檢測、統計分析、流量日志、白名單檢測為主。

1. 特征檢測

特征檢測是通過安全專家經驗從已知Webshell樣本中提取惡意特征進行模式匹配檢測,也有一些是自動化提取特征或用md5檢測,優點在於檢測速度快,部署方便。缺點不言而喻,需要持續性維護正則匹配庫,隨着量的積累,維護成本大,並且誤報率高、泛化能力弱、不能發現未知威脅,在檢測率與誤報方面難以平衡。

2. 統計分析

統計分析是利用一些統計學方法進行Webshell識別與檢測,通過提取文件中的特征代碼、信息熵、最長單詞、重合指數、壓縮等特征進行異常檢測,這種方法對某些混淆、變形的Webshell文件具有很好的識別效果,但是誤報和漏報特別多。業界也有一些開源項目可以參考,如NeoPI。

3. 機器學習

機器學習方法也是當前Webshell檢測的研究熱點,利用決策樹、SVM、LSTM、深度學習等方法對樣本訓練得到檢測模型,機器學習模型的優點是具備一定未知樣本的發現能力。缺點是模型建設對樣本的要求比較高,另一個是機器學習模型看的指標是看准確率和誤報率比例,但在安全運營上除了比例絕對數量值也非常重要,文件量大誤報率即便很低告警數還是會很大難以運營,從我們的實踐來看機器學習模型配合其他方法一起使用效果更佳。

4. 動態檢測

動態檢測通過監控代碼的行為來判斷是否為webshell,監控代碼行為多采用RASP方式,檢測或阻斷風險操作。無論代碼如何變形混淆行為是不變的,這種方法可以有效的檢測混淆、變形木馬,准確率高,但是RASP是串行模式部署,監控行為需要占用到業務一些資源,從我們的實踐來看監控點要做分級,根據業務情況動態調整。

5. 流量日志檢測

基於流量日志的分析方法也是企業常用的一種檢測方案,通過流量分析行為,結合訪問日志、數據包內容等信息進行建模分析,從中挖掘可能出現的的異常點。例如:一個上傳的數據包中存在異常的文件名后綴或者HTTP請求中出現比較可疑的payload,這些都可能是一個Webshell的行為特征。基於流量日志檢測的好處是客戶端無需部署Agent,缺點是漏報多,同時如果是HTTPS等加密流量就比較麻煩。

6. 白名單檢測

采用非白即黑的方式進行惡意檢測,白名單文件可以基於代碼庫或者發布平台收集,這種方法的優點是檢測率高,不需要安全特征系統並且開發維護成本低,缺點是如果開發人員下載了帶后門的代碼或者內部員工留后門會繞過檢測。
上述介紹的方案每個公司的情況不一樣落地效果也不一樣,可能在騰訊不適合的方案在其他場景就很好用,具體技術方案的選擇還是需要依據具體情況決定。總而言之,各種檢測方法都有自身擅長的檢測方向,短板也非常明顯。針對上述介紹的方案,洋蔥檢測引擎均有涉及並應用。在實際的應用中,應當取長補短,相得益彰,才能發揮安全防御的最大成效。為此,洋蔥團隊深入研究了Webshell的特點,並提出了一種更為有效的檢測方案-基於語義和污點追蹤+動態模擬執行+機器學習的檢測模型,具有高檢測率、低漏報、可解釋性強等特點,對未知Webshell、變形Webshell的檢測能力尤為突出。

洋蔥語義動態檢測引擎

洋蔥Webshell檢測引擎以語義污點分析為基礎,結合動態模擬執行,同時利用機器學習模型作為輔助判斷,具有高檢測率、低漏報、可解釋性強等特點,對未知Webshell、變形Webshell的檢測能力尤為突出。

洋蔥檢測引擎主要由引擎層、數據層、策略層、應用層四部分構成,整體架構如下圖所示:

本文將主要介紹語義污點分析、動態模擬執行的過程,以語義分析為虛、動態模擬執行為實,最后采用虛實結合的策略完成Webshell的檢測,整體分析流程如下:

檢測引擎本質上是基於污點分析的思想進行語義、動態分析,這里先簡單介紹下污點分析的概念。
污點分析可以抽象成一個三元組<sources,sinks,sanitizers>的形式,

sources:污染源,表示所有用戶可控的輸入點。
sinks:危險匯聚點,代表所有可能產生威脅的函數或操作。
sanitizers:無害處理,代表進行安全操作或過濾,使得樣本不在具有安全風險。

洋蔥檢測引擎的語義、動態分析過程有所不同,語義污點追蹤是從sinks觸發,判斷sinks中的變量或函數是否存在外界可控,而動態污點分析是從sources觸發,判斷這些污染源是否下沉至sinks點。

語義污點追蹤


語義污點追蹤先是對樣本進行預處理,保證樣本文件可以正常的解析為AST抽象語法樹,之后尋找所有的sink點,從這些sinks出發,經過靜態追蹤判斷sink點是否與外接輸入相關。它是一種靜態檢測方法,不會真正的執行代碼,只能分析代碼表象,故為虛。流程圖如下:

1. AST
AST(抽象語法樹)是PHP 7 引入的一個新特性,在PHP收到一個請求或執行命令時,會首先進行詞法和語法分析,生成AST,再生成字節碼opcodes,繼而繼續執行並返回結果。正是由於AST的出現,我們對復雜多變的PHP Webshell的檢測有質的提升。

形如最經典的shell一句話:

生成的抽象語法樹如下(生成過程可參考https://astexplorer.net/):

2. 靜態污點分析

靜態污點分析過程可以分為三個部分,危險匯聚點、污染傳播和污染源。

(1)危險匯聚點
在對樣本解析生成語法樹之后,第一步便是要尋找萬惡之源-sinks,檢測引擎匯集了幾乎所有可能存在的風險點,一共可以分為代碼執行、文件操作、動態操作、命令執行、外部組件、回調等。我們首先基於一個認知,所有的webshell,無論如何變形,都擺脫不了對上述風險點的利用。

(2) 污點傳播

在收集了所有威脅參數節點之后,便針對這些節點進行逐一回溯追蹤判斷,進而判斷數據流是否可控。洋蔥檢測引擎支持變量賦值、引用傳值、函數調用、函數賦值、間接傳值等多種方式。

(3) 污染源

污染源是第二步威脅節點追蹤的的終點,如果在威脅節點追蹤的過程中發現是外界可控參數或與外界可控變量相關,則直接判斷為惡意webshell。常見的污染源有預定義變量、可控函數、變量覆蓋函數等。

(4) 檢測示例

function test($a){
eval($a);
}
$a = $_GET["c"];
test($a);

上述代碼是一種函數調用型webshell,語義追蹤的過程為:首先發現eval危險函數,之后追蹤eval參數$a,發現是在函數test中,然后再跟蹤test函數的調用信息,最后鎖定調用參數是否外部可控,其數據流模型圖為:

間接影響的shell樣本如下:
foreach ($_GET as $b){
if ($b == "c"){
$e = 'e';
}
if ($b == "m"){
$e .= 'val($_GET[1]);';
}
// ...
}
eval($e);

通過外界輸入間接控制變量進而構造出webshell文件。靜態分析的過程為:追蹤$e變量,發現在賦值節點中會被$b影響,並且$b變量受到外界控制,則判斷為webshell文件。在這種情況下,洋蔥檢測引擎會提取所有與變量相關聯的節點進行回溯,確保關聯節點也不會收到外界影響,對應的數據流模型圖為:

動態模擬執行


動態模擬執行部分是基於參數賦值+污點分析的方式實現的。模擬執行可以通過在運行時對外界參數進行模擬賦值的方式,使得代碼可在不需要外界參數的情況下繼續運行下去。而污點分析技術,可以將模擬值(Source點)標記為污點數據,通過追蹤污點數據相關的信息是否流向敏感函數(Sink點),以此來發現可通過外界參數控制敏感函數參數的風險行為。

模擬執行檢測過程可以分成4個階段:

(1) 參數賦值

參數賦值是模擬執行的核心,首先尋找全局外部變量,如GET、POST、COOKIE等。然后通過符號計算推導出符合程序條件運行的虛擬值,並在運行時進行賦值,使得程序可在不需要外部參數的情況下運行起來。

(2) 污點標記

污點標記是整個模擬執行檢測的前提,目前在不同的應用程序中進行污點標記的方法各不相同,php這里是借鑒了taint的實現方式,利用php變量的特殊標記和預留標志位,將標志位打點實現標記。污點標記是與參數賦值相結合的,賦值的參數在運行階段便被打上標記。

(3) 污點傳播

污點傳播是模擬執行檢測的保障,對於已被打上標記的參數變量,變量帶有的污點會在程序流中傳遞,但在傳遞過程中可能存在污點丟失的情況,所以需要對字符串處理函數、加密函數和轉換函數等進行處理,檢測函數參數是否存在污點,若存在則將返回值打上標記,以保證污點標記向下傳播。

(4) 污點檢測

污點檢測的核心點是敏感函數划分,如何判定為敏感函數是模擬執行檢測的重點,首先要關注的是命令執行函數和代碼執行函數,其次是可間接調用函數的動態調用、反射調用和回調類型,最后是風險程度較小的文件操作、數據庫操作等。確定好敏感函數后,通過檢測敏感函數的參數是否帶有污點來確定是否為惡意文件。核心的檢測邏輯便是敏感函數+可控參數。 

 


以下面例子簡述模擬執行檢測的運行原理

$a = $_GET['code'];
$b = base64_decode($a);
eval($b);
?>

上述代碼是一個簡單的Webshell,參數經過base64解密后進行代碼執行。動態模擬執行的過程為:首先尋找到外部變量,對外部變量$_GET['code'] 進行參數賦值且給$_GET['code'] 打上標記,然后賦值給$a。第二步是將$a用base64解密后賦值給$b,由於base64_decode hook后會檢測參數是否存在污點,有則將返回值打上標記,故$b帶有污點標記。最后經過Sink點(eval)執行且參數$b帶有污點, 故判定為惡意文件。其數據流模型圖為:

組合分析


靜態的符號執行只能分析出代碼所能呈現的邏輯結構,並不知道具體變量的內容。PHP復雜多變的動態特性是影響靜態分析的關鍵性因素。比如在動態調用檢測中,單純的語義只能分析出存在動態調用函數,並不清楚具體是哪個函數調用,但直接告警很明顯是不合理的,因此必須要動態的模擬執行配合才能確切的判斷是否為惡意文件,其次在檢測代碼執行時,語義解析由於不清楚變量中的內容導致無法解析具體執行的代碼內容,這一部分也是需要動態配合進行檢測。

其次雖然動態檢測在對抗靜態混淆繞過方面具備相對優秀的檢測能力,但在實現動態檢測的過程中,還是遇到許多困難點,相對於傳統正則引擎,這種方式也多了一些比較另類的繞過可能,總結這些繞過其核心的思路有以下兩點:

1. 打斷污點傳播,使得經過Sink點的參數不具備污點,繞過檢測。
2. 改變程序執行流,使得程序流在不傳入特定值時不經過Sink點,從而繞過檢測。

靜態語義檢測和動態污點檢測都有其難以彌補的短板問題,發現問題才能解決問題,有對抗才能有提升。洋蔥檢測引擎建設了許多動靜結合的策略,在保證低誤報的前提下,盡可能的發掘代碼中的威脅行為。靜態語義分析主要為了保證動態污點能夠准確的流向危險源,而動態模擬執行能提供函數調用鏈用於靜態語義分析,這便是兩者思想的結合點。

動靜結合可以有效的解決代碼中的分支問題,抽象語法樹解析可以正確的識別分支邏輯,並把可能存在風險的分支內容扔給動態去處理。

關於報錯終止問題,靜態可先將代碼封裝為try-catch的方式,將報錯代碼忽略,而后交給動態,保證程序的繼續進行。

關於變量覆蓋問題,則可通過hook影響變量定義或賦值的函數來覆蓋。
同時引擎也會提取動靜態的檢測結果進行整合分析,比如代碼如下:

function test(){
/*
* eval code
* */
}
$a = 't'.'e'.'s'.'t';
$a();

靜態發現一個危險函數test,但是沒有找到調用信息,而動態走到了這個危險函數中,通過兩個檢測結果的對比與結合,便可判斷其為一個Webshell文件。當然就結合方面,引擎還有一段很長的路要走。

對抗與思考


檢測過程解剖完成之后,對抗的思路也就變得很清晰了。檢測引擎可能面臨敏感源、污點傳播、污染源三方面的對抗。

1. 敏感源

檢測引擎的敏感源必須考慮完備,如果出現一些函數,形如mb_eregi_replace、set_error_handler 等,可以執行代碼並且不在引擎的監控列表內,那么就可以直接繞過引擎的檢測,這是引擎可能面臨的威脅之一。

例:mb_eregi_replace
mb_eregi_replace('.*',$_GET[1],'','e');
當mb_eregi_replace的options為e時,可以執行進行代碼執行。

2. 污染傳播

通過打斷污染傳播來構造webshell是一個非常常用且有效的繞過手段,也是檢測引擎監控和防御的難點。打斷污點的方式很多,比如利用報錯、分支、間接影響、環境等等方式。

例:環境變量
putenv($_GET["c"]);
eval(getenv('path'));
?>

首先通過putenv傳遞變量,之后獲取變量中的path內容,那么只需要傳入c=path=phpinfo();即可完成利用。

例:變量覆蓋
define($_GET[a], $_GET[b]);
eval(A);
?>
先外部傳入a=A 定義A變量,再通過傳入b=phpinfo();給A變量賦值,即可完成利用,因此這就要求安全人員盡可能盡可能覆蓋所有的變量傳播方式,同時這也是一個積累與完善的過程。

3. 污染源

通過尋找冷門的污染源進而繞過檢測,也是檢測引擎的風險點之一。比如filter_input也可以接受外部參數,如下例中通過傳入參數c即可執行代碼。

$a = filter_input(INPUT_GET,'c');
eval($a);
?>

總結


在靜態語法樹分析方面,其實也可以實現局部的模擬執行功能,還原基本的變量,減輕動態檢測的負擔。在動態檢測方面,可以結合優質的求解器等符號執行方法。

玉不琢,不成器。檢測引擎的提升是一個不斷沉淀與積累的過程,只有不斷的積累攻擊手法,舉一反三,才能不斷的提升檢測能力和檢測質量。通過這次比賽,我們也學習到了白帽子各種新鮮的技術,這也極大的提升了引擎的檢測能力,同時也認識到檢測引擎的不足,我們會對引擎更加細致的升級與改造。青山不改,綠水長流,我們后會有期!

感謝海星、海章對本文以及檢測引擎建設的幫助與指導,感謝洋蔥團隊、研發團隊、TSRC、騰訊藍軍、運營團隊對檢測引擎和本次活動的支持。

我們站在巨人的肩膀上,感謝熾天使、牛哥、職業欠錢、flyh4t、cradmin、劉水生、岳志飛、黃湘琦博士、達達、kb、snaker、huili、momo、zhixiang等前輩對洋蔥webshell檢測引擎的貢獻。

最后再次感謝本次參與測試的白帽子們(排名不分先后):phithon,zsp,Omegogogo,w,goto:REinj,do9gy,蜂花護發素,白帽醬,鯨落,紫跡,星光,wuyx,tharavel,test123,monkeyd,impakho,fzxcp3,Lenka

參考

  1. laruence. taint: https://github.com/laruence/taint
  2. 王蕾, 李豐, 李煉,等. 污點分析技術的原理和實踐應用[J]. 軟件學報, 2017, 028(004):860-882.
  3. PHP. https://www.php.net/
  4. Webshell通用免殺思考:https://www.freebuf.com/company-information/235706.html
  5. 青藤雲安全|騰訊lake2 :Webshell檢測的前世今生. https://zhuanlan.zhihu.com/p/136905818 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM