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/.exe|KAdot@ngs/.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_EVAL,ZEND_DO_FCALL,ZEND_DO_FCALL_BY_NAME(参见下面举例函数)等处理函数在扩展中使用zend_set_user_opcode_handler进行处理。
攻击者通过任意文件上传漏洞将Webshell上传到目录中。那么在文件上传后进行访问时,通过对文件所在路径是否在黑白名单中进行判断,如果不符合黑白名单规则,则视为攻击并及时拦截。
潜在的可以HOOK的危险函数:
- 命令执行类:passthru,system,popen,exec,shell_exec等
- 文件系统类:fopen,opendir,dirname,pathinfo等
- 数据库操作类:mysql_query,mysqli_query等
- 回调函数:array_filter,array_reduce,usort,uksort等
- 反射函数: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表现无力。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/55981.html