精确查找PHP WEBSHELL木马的方法(1)


Posted in Python onApril 12, 2011

先来看下反引号可以成功执行命名的代码片段。代码如下:

`ls -al`; 
`ls -al`; 
echo "sss"; `ls -al`; 
$sql = "SELECT `username` FROM `table` WHERE 1"; 
$sql = 'SELECT `username` FROM `table` WHERE 1' 
/* 
无非是 前面有空白字符,或者在一行代码的结束之后,后面接着写,下面两行为意外情况,也就是SQL命令里的反引号,要排除的就是它。 
*/

正则表达式该如何写?
分析:
对于可移植性的部分共同点是什么?与其他正常的包含反引号的部分,区别是什么?
他们前面可以有空格,tab键等空白字符。也可以有程序代码,前提是如果有引号(单双)必须是闭合的。才是危险有隐患的。遂CFC4N给出的正则如下:【(?:(?:^(?:\s+)?)|(?:(?P<quote>["'])[^(?P=quote)]+?(?P=quote)[^`]*?))`(?P<shell>[^`]+)`】。
解释一下:
【(?:(?:^(?:\s+)?)|(?:(?P<quote>["'])[^(?P=quote)]+?(?P=quote)[^`]*?))】
匹配开始位置或者开始位置之后有空白字符或者前面有代码,且代码有闭合的单双引号。(这段PYTHON的正则中用了捕获命名以及反向引用)
【`(?P<shell>[^`]+)`】这个就比较简单了,匹配反引号中间的字符串。
精确查找PHP WEBSHELL木马的方法(1)
某检测PHP webshell的python脚本考虑欠佳。
再看看下一个列表的第一个元素。【(system|shell_exec|exec|popen)】,这个正则的意思是只要字符串里包含“system”、“shell_exec”、“exec”、“popen”这四组字符串即判定为危险字符。很明显,这个方法太不严谨。如果程序员写的代码中,包含了这四组字符,即可被判定为危险函数。很不准确,误报率极高。见下图
精确查找PHP WEBSHELL木马的方法(1)
某检测PHP webshell的python脚本考虑欠佳。
到底什么样的代码是可疑的代码?关键词是什么?

可疑的代码肯定是由可以执行危险操作的函数构成,可以执行危险操作的PHP函数最重要的就是“eval”函数了,对于加密的PHP代码(仅变形字符串,非zend等方式加密),肯定要用到“eval”函数,所以,对于不管是用哪种加密方法的代码,肯定要用到“eval”函数。其次就是可以执行系统命令的函数了,比如上面某牛的代码中提到的四个“system”、“shell_exec”、“exec”、“popen”。当然还有其他的,比如passthru等。PHP还支持“·”字符(ESC键下面那个)直接执行系统命令。我们可以把正则写成这样【\b(?P<function>eval|proc_open|popen|shell_exec|exec|passthru|system)\b\s*\(】。
精确查找PHP WEBSHELL木马的方法(1)
检测PHP webshell的python脚本相对较为严谨的匹配
解释一下:

大家都知道【\b\b】用来匹配单词两边的位置的。要保证【\b\b】中间的是单词,即使函数名前面加特殊字符,也一样通过匹配,比如加@来屏蔽错误。后面的【\s*】用来匹配空白字符的,包括空格,tab键,次数为0到无数次。前面的【(?P)】是捕获命名组。用来当作python代码直接引用匹配结果的key。

还有的网友提到了,如果我把代码放到图片拓展名的文件里呢?那你只检测.php,.inc的文件,还是找不到我的呀。嗯,是的,如果恶意代码在gif、jpg、png、aaa等乱七八糟的拓展名文件里,是不能被apache、IIS等web Services解析的,必须通过include/require(_once)来引入。那么,我们只要匹配include/require(_once)后面的文件名是不是常规的“.php”、“.inc”文件。如果不是,则为可疑文件。正则如下【(?P<function>\b(?:include|require)(?:_once)?\b)\s*\(?\s*["'](?P<filename>.*?(?<!\.(?:php|inc)))["']】。
精确查找PHP WEBSHELL木马的方法(1)
检测PHP WEBSHELL的python脚本较为严谨做法
解释一下:

先看【(?P<function>\b(?:include|require)(?:_once)?\b)】,【(?P<name>)】为正则表达式的“命名捕获”,PHP中有同样的用法。也就是说,在这括号内的捕获的数据,会分配到结果数组的key为“name”的value中。再看里面的【\b(?:include|require)(?:_once)?\b】,【\b\b】不解释了,为单词边界位置。里面的【(?:include|require)】匹配字符串“include”、“require”两个单词,其中前面的【(?:)】未不分配组,用于提高效率,可以去掉【?:】变成【(include|require)】。在后面一个【(?:_once)】也是做不分配组的操作,便于提高正则表达式效率。同样,后面的量词是“?”代表这个组可有可无。就满足了“include”、“include_once”、“require”、“require_once”四种情况。有的朋友可能这样写【(include|include_once|require|require_once)】也能实现目的。但是,为了更搞的效率,我们对这个正则做优化,针对部分字符串做分支更改,改成上面那个【\b(?:include|require)(?:_once)?\b】。

再看下面的【\s*\(?\s*["'](?P<filename>.+?(?<!\.(?:php|inc)))["']】中,【\s*】匹配空白字符,包括空格,tab键等。后面的【\(?】,匹配字符“(”,后面的量词“?”表示这半个小酷括号可有可无。防止“incude “123.php””这种没有括号的情况。再后面【["']】匹配双引号,单引号的。最后的也是。再看看这个【(?P<filename>.+?(?<!\.(?:php|inc)))】,其中【(?P<filename>)】上面介绍了,为命名捕获,把结果放到match.group(“filename”)里。【.*?】为任意字符,后面的量词是“忽略优先量词”,也就是平常说的“非贪婪”。这里最少匹配零个,(防止.aa、.htaccess这种没有文件名,只有文件拓展名的文件被引入)。后面的【(?<!\.(?:php|inc))】,这里用到了反向零宽断言(环视)的非操作(只匹配位置,不匹配字符串,跟【^$\b】等一样)。这个表达式是针对这个位置的后面字符起作用的,也就是说后面的【["']】的前面不能是“.php”、“.inc”,这里也就是取了文件名的最后的拓展名。(正则里,可以用【^】对字符取非,但是不能对“字符串组”取非,这里用了零宽断言来实现。)

综上所述,最后,鄙人给出的python代码如下:

#!/usr/bin/python 
#-*- encoding:UTF-8 -*- 
### 
## @package 
## 
## @author CFC4N <cfc4nphp@gmail.com> 
## @copyright copyright (c) Www.cnxct.Com 
## @Version $Id: check_php_shell.py 37 2010-07-22 09:56:28Z cfc4n $ 
### 
import os 
import sys 
import re 
import time 
def listdir(dirs,liston='0'): 
flog = open(os.getcwd()+"/check_php_shell.log","a+") 
if not os.path.isdir(dirs): 
print "directory %s is not exist"% (dirs) 
return 
lists = os.listdir(dirs) 
for list in lists: 
filepath = os.path.join(dirs,list) 
if os.path.isdir(filepath): 
if liston == '1': 
listdir(filepath,'1') 
elif os.path.isfile(filepath): 
filename = os.path.basename(filepath) 
if re.search(r"\.(?:php|inc|html?)$", filename, re.IGNORECASE): 
i = 0 
iname = 0 
f = open(filepath) 
while f: 
file_contents = f.readline() 
if not file_contents: 
break 
i += 1 
match = re.search(r'''(?P<function>\b(?:include|require)(?:_once)?\b)\s*\(?\s*["'](?P<filename>.*?(?<!\.(?:php|inc)))["']''', file_contents, re.IGNORECASE| re.MULTILINE) 
if match: 
function = match.group("function") 
filename = match.group("filename") 
if iname == 0: 
info = '\n[%s] :\n'% (filepath) 
else: 
info = '' 
info += '\t|-- [%s] - [%s] line [%d] \n'% (function,filename,i) 
flog.write(info) 
print info 
iname += 1 
match = re.search(r'\b(?P<function>eval|proc_open|popen|shell_exec|exec|passthru|system)\b\s*\(', file_contents, re.IGNORECASE| re.MULTILINE) 
if match: 
function = match.group("function") 
if iname == 0: 
info = '\n[%s] :\n'% (filepath) 
else: 
info = '' 
info += '\t|-- [%s] line [%d] \n'% (function,i) 
flog.write(info) 
print info 
iname += 1 
f.close() 
flog.close() 
if '__main__' == __name__: 
argvnum = len(sys.argv) 
liston = '0' 
if argvnum == 1: 
action = os.path.basename(sys.argv[0]) 
print "Command is like:\n %s D:\wwwroot\ \n %s D:\wwwroot\ 1 -- recurse subfolders"% (action,action) 
quit() 
elif argvnum == 2: 
path = os.path.realpath(sys.argv[1]) 
listdir(path,liston) 
else: 
liston = sys.argv[2] 
path = os.path.realpath(sys.argv[1]) 
listdir(path,liston) 
flog = open(os.getcwd()+"/check_php_shell.log","a+") 
ISOTIMEFORMAT='%Y-%m-%d %X' 
now_time = time.strftime(ISOTIMEFORMAT,time.localtime()) 
flog.write("\n----------------------%s checked ---------------------\n"% (now_time)) 
flog.close() 
## 最新代码在文章结尾的链接里给出了。2010/07/31 更新。

仅供参考,欢迎斧正。

下面截图为扫描Discuz7.2的效果图,当然,也有误报。相对网上流传的python脚本,误报更少,更精确了。 精确查找PHP WEBSHELL木马的方法(1)
检测PHP WEBSHELL的python脚本的检测结果
问:这个方法完美了吗?可以查找目前已知的所有危险函数文件了吗?
答:不能,如果include等引入的文件没有拓展名,这里就匹配不到了。
问:如何解决?
答:留给你解决,聪明的你,肯定可以搞定。
PS:“`”反引号 执行命令的还没写,暂时没好的办法。容易跟SQL语句中的反引号混淆。不太好匹配。如果光匹配反引号就提示的话,那误报太大了。待定吧。(术业有专攻,请勿因为一处不好的代码,否定一个人的能力。你懂的。再次重申,此文只针对代码,不针对人。其次,鄙人给出的python代码随便复制,随便传播,爱留版权就留版权,不爱留就删了相关字符,也就是您爱干吗干吗。)
我先休息一会,明天再说。(前半句为三国杀曹仁的台词,哈。)

Python 相关文章推荐
Python实现批量将word转html并将html内容发布至网站的方法
Jul 14 Python
在Django的上下文中设置变量的方法
Jul 20 Python
Python实现完整的事务操作示例
Jun 20 Python
django 创建过滤器的实例详解
Aug 14 Python
python将一组数分成每3个一组的实例
Nov 14 Python
Python字符串的一些操作方法总结
Jun 10 Python
python 求一个列表中所有元素的乘积实例
Jun 11 Python
Python K最近邻从原理到实现的方法
Aug 15 Python
PyQt5+Caffe+Opencv搭建人脸识别登录界面
Aug 28 Python
python matplotlib折线图样式实现过程
Nov 04 Python
基于python实现学生信息管理系统
Nov 22 Python
Python爬虫爬取煎蛋网图片代码实例
Dec 16 Python
Python中删除文件的程序代码
Mar 13 #Python
python 中文乱码问题深入分析
Mar 13 #Python
学习python处理python编码问题
Mar 13 #Python
布同 Python中文问题解决方法(总结了多位前人经验,初学者必看)
Mar 13 #Python
布同 统计英文单词的个数的python代码
Mar 13 #Python
python将多个文本文件合并为一个文本的代码(便于搜索)
Mar 13 #Python
布同自制Python函数帮助查询小工具
Mar 13 #Python
You might like
在PHP中使用curl_init函数的说明
2010/11/02 PHP
Yii学习总结之数据访问对象 (DAO)
2015/02/22 PHP
php实现的二叉树遍历算法示例
2017/06/15 PHP
基于jQuery的ajax功能实现web service的json转化
2009/08/29 Javascript
在chrome中window.onload事件的一些问题
2010/03/01 Javascript
JavaScript 事件系统
2010/07/22 Javascript
javascript 实现 秒杀,团购 倒计时展示的记录 分享
2013/07/12 Javascript
巧用局部变量提升javascript性能
2014/02/24 Javascript
jQuery实现弹出窗口中切换登录与注册表单
2015/06/05 Javascript
基于insertBefore制作简单的循环插空效果
2015/09/21 Javascript
jQuery日历插件datepicker用法详解
2016/03/03 Javascript
JavaScript中实现无缝滚动、分享到侧边栏实例代码
2016/04/06 Javascript
AngularJs ng-route路由详解及实例代码
2016/09/14 Javascript
浅谈regExp的test方法取得的值变化的原因及处理方法
2017/03/01 Javascript
JavaScript中click和onclick本质区别与用法分析
2018/06/07 Javascript
angularJs中ng-model-options设置数据同步的方法
2018/09/30 Javascript
vue-cli webpack配置文件分析
2019/05/20 Javascript
关于layui的下拉搜索框异步加载数据的解决方法
2019/09/28 Javascript
vue通过接口直接下载java生成好的Excel表格案例
2020/10/26 Javascript
详解ES6 扩展运算符的使用与注意事项
2020/11/12 Javascript
python解析xml模块封装代码
2014/02/07 Python
Python 判断是否为质数或素数的实例
2017/10/30 Python
Python简单生成随机数的方法示例
2018/03/31 Python
WIn10+Anaconda环境下安装PyTorch(避坑指南)
2019/01/30 Python
Python函数参数匹配模型通用规则keyword-only参数详解
2019/06/10 Python
用python读取xlsx文件
2020/12/17 Python
Python读取ini配置文件传参的简单示例
2021/01/05 Python
Helly Hansen工作服美国官方网上商店:为最恶劣的环境
2019/09/04 全球购物
俄罗斯童装网上商店:BebaKids
2020/06/06 全球购物
意大利领先的奢侈品在线时装零售商:MCLABELS
2020/10/13 全球购物
公司感恩节活动策划书
2014/10/11 职场文书
三好学生评语大全
2014/12/29 职场文书
2016年春节慰问信息
2015/03/25 职场文书
MySQL优化常用的19种有效方法(推荐!)
2022/03/17 MySQL
Nginx如何配置多个服务域名解析共用80端口详解
2022/09/23 Servers
CSS list-style-type属性使用方法
2023/05/21 HTML / CSS