基於PHP的Webshell自動檢測芻議

wyzsk發表於2020-08-19
作者: WotChin · 2015/12/31 14:51

0x00 引言


看到wooyun知識庫上眾多大神的精彩文章,深感自身LOW到爆,故苦思冥想找來一個題目,主要求邀請碼一枚,以便以後繼續學習。

對於網路維護人員來說,恐怕最頭痛的就是網站被黑,還留下了後門,甚至連伺服器都被提權。而Hacker通常在相對不易引人注目的地方留下後門。逐個排查感覺很吃力,那麼,本文姑且探討一下在PHP環境下的Webshell自動檢測的可行方案。

0x01 關鍵詞檢測


利用正規表示式等對一些常見的Webshell關鍵詞進行掃描,從而判斷出該檔案是否為Webshell.這種檢測方法真是太暴力了,也是最簡單最傳統的檢測方式。顯而易見,這樣簡單粗暴地檢測會產生較高的誤報率,而且對於一些加了密或者變形的Webshell又會出現檢測不出來的問題。所以,這樣篩選出來的Webshell還要經過網路維護人員的手動核實,查殺原則是:寧可錯殺一百,不可放過一個。。。。

譬如下面這段在某網站Get的程式碼,據稱是某線上Webshell查殺真實程式碼:

#!php
class scan{
private $directory = '.';
private $extension = array('php');
private $_files = array();
private $filelimit = 5000;
private $scan_hidden = true;
private $_self = '';
Private $_regex='(preg_replace.*\/e|`.*?\$.*?`|\bcreate_function\b|\bpassthru\b|\bshell_exec\b|\bexec\b|\bbase64_decode\b|\bedoced_46esab\b|\beval\b|\bsystem\b|\bproc_open\b|\bpopen\b|\bcurl_exec\b|\bcurl_multi_exec\b|\bparse_ini_file\b|\bshow_source\b|cmd\.[email protected]\.ru|小組|專用|提權|木馬|PHP\s?反彈|shell\s?加強版|WScript\.shell|PHP\s?Shell|Eval\sPHP\sCode|Udp1-fsockopen|xxddos|Send\sFlow|fsockopen\('(udp|tcp)|SYN\sFlood)';
private $_shellcode='';
private $_shellcode_line=array();
private $_log_array= array();
private $_log_count=0;
private $action='';
private $taskid=0;
private $_tmp='';

0x02 對混淆Webshell的判斷

1. 資訊熵

說到對加密的Webshell的判斷,就不得不提到資訊理論。夏農資訊理論的基本點是用隨機變數或隨機向量來表示信源,運用機率論和隨機過程的理論來研究資訊。經過編碼的Webshell檔案包含大量隨機內容或特殊資訊,這種檔案將產生更多的ASCII碼,使用ASCII碼計算檔案的熵值會變大,即衡量了Webshell對於普通檔案的不確定性。

公式說明:

  • 其中,n為ASCII碼,對於ASCII為127的字元(空格)的判斷無意義,去除;Xn為第n位ASCII碼在當前檔案中出現的次數,S為當前指令碼檔案的總字元數。
  • 熵值Info(A)越大,為Webshell的可能性越大。

有關資訊熵的更多內容,可以參考:https://en.wikipedia.org/wiki/Entropy_%28information_theory%29

2. index of coincidence(IC,重合指數)

這裡再使用一種方法:設X為一個長度為n的密文字串,我們用集合來表示這個密文字串{X1,X2,…,Xn},X的重合指數是指隨機抽取任意兩個元素相同的機率。

設Ni為字元i在這份密文中出現的次數。從n個密文字元中抽取兩個字元的方式有

而其中Ni個i組成一對的方式有

於是,兩式作比,即為從X中抽到兩個字元都為i的機率。

加密檔案的密文隨機性變大,其重合指數變低。編碼後的Webshell類似於隨機檔案,而明文的Webshell由於具有用於提權的類似隨機字串或者包含二進位制、十六進位制序列的內容,因此使用擴充套件的ASCII碼作為研究物件,即計算254個字元(除去ASCII為127)的重合指數。那麼對於指令碼檔案,如果重合指數越低,則為Webshell的可能性越大。

除上述介紹過的兩種演算法,還可以使用base64編碼待檢測指令碼檔案,對於加密的Webshell檔案,由於base64編碼消除了非ASCII的字元,這樣實際上經base64編碼過的件的字元就會具有這樣的特徵——更小且出現分佈的不均衡,也就是說檔案的壓縮比會變大。透過這樣的方式來檢測Webshell的特徵就是相對其他檔案,具有更大的壓縮比。

對於混淆的Webshell的判斷,根據演算法算出來的結果都是一些具體的數值,要根據實際設定好的閾值來進行比較,從而來判斷是否為Webshell。而閾值的設定由於不同的網站並不相同,因此還要經過具體的測試。

0x03 基於PHP擴充套件的Webshell實時動態檢測


這種檢測方法在目前比較流行,主要由於該方法採用對PHP呼叫危險函式的HOOK,能夠動態地檢測出Webshell,相對比較實時,迅速。在一定程度上,彌補了傳統Webshell靜態檢測的不足,也相對更為方便。

PHP在WEB容器內的執行方式主要有三種:模組載入方式執行,CGI或FastCGI方式執行,三種方法都要經過5個階段:模組初始化,請求初始化,程式碼執行,請求結束,模組結束。在PHP的程式碼執行過程中,透過詞法分析,將PHP程式碼轉變為語言片段(tokens),然後語法分析轉化為有意義的表示式,最後將表示式編譯為中間位元組碼(opcodes).中間位元組碼在Zend虛擬機器執行,然後輸出結果。

我們透過使用PHP核心提供的通用介面:zend_set_user_opcode_handler來修改中間位元組碼對應的處理函式,達到對PHP核心HOOK的效果。 函式原型:

#!c
int zend_set_user_opcode_handler(zend_uchar opcode,opcode_handler_t handler)

前者為需要的opcode;後者為hook後的處理函式。

一般將ZEND_INCLUDE_OR_EVALZEND_DO_FCALLZEND_DO_FCALL_BY_NAME(參見下面舉例函式)等處理函式在擴充套件中使用zend_set_user_opcode_handler進行處理。

攻擊者透過任意檔案上傳漏洞將Webshell上傳到目錄中。那麼在檔案上傳後進行訪問時,透過對檔案所在路徑是否在黑白名單中進行判斷,如果不符合黑白名單規則,則視為攻擊並及時攔截。

潛在的可以HOOK的危險函式:

  1. 命令執行類:passthru,system,popen,exec,shell_exec等
  2. 檔案系統類:fopen,opendir,dirname,pathinfo等
  3. 資料庫操作類:mysql_query,mysqli_query等
  4. 回撥函式:array_filter,array_reduce,usort,uksort等
  5. 反射函式:ReflectionFunction

PHP擴充套件采用純C編寫,給出主要程式碼供參考:

#!c
#include“config.h”
#include“php.h”
#include“php_ini.h”
#include“ext/standard/info.h”
#include“php_waf.h”
static int le_waf;
const zend_function_entry waf_functions[] = {
PHP_FE(confirm_waf_compiled,NULL),PHP_FE_END };
zend_module_entry waf_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
“waf”,
waf_functions,
PHP_MINIT(waf),
PHP_MSHUTDOWN(waf),
PHP_RINIT(waf),
PHP_RSHUTDOWN(waf),
PHP_MINFO(waf),
#if ZEND_MODULE_API_NO >= 20010901
PHP_WAF_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_WAF
ZEND_GET_MODULE(waf);
#endif
PHP_MINIT_FUNCTION(waf)
{ zend_set_user_opcode_handler(ZEND_INCLUDE_OR_EVAL,manage); // hook eval等
zend_set_user_opcode_handler(ZEND_DO_FCALL_BY_NAME,manage); //hook變數函式執行
zend_set_user_opcode_handler(ZEND_DO_FCALL,manage); //hook命令執行
return SUCCESS;
}
int manage() /*HOOK處理函式*/
{ char* filepath = (char*)zend_get_executed_filename(TSRMLS_C);
if(strstr(filepath,”upload”))/*判斷字串“upload”是否是filepath的子串*/
{ php_printf(“請不要執行惡意程式碼<br>執行檔案路徑 :%s”,filepath);
return ZEND_USER_OPCODE_RETURN;}
else
return ZEND_USER_OPCODE_DISPATCH;
}

0x04 總結


除上述所採用的Webshell檢測方法,目前還有基於網路的檢測方式等。

例如,目前的研究有集中於在網路入口處配置IDS或者WAF來檢測Webshell的方法。Fireeye[28]提出使用Snort配置特徵規則來檢測一句話木馬.另有透過配置ModSecurty的核心規則集(CoreRule Sets)來檢測上傳Webshell的行為的方式。

上述兩種方法都是透過分析http請求中是否包括特殊關鍵詞(例如<form<%<?<php等)來判斷攻擊者是否正在上傳HTML或者指令碼檔案。這種判斷方式對於已經存在的Webshell表現無力。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章