精确查找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 相关文章推荐
centos系统升级python 2.7.3
Jul 03 Python
用python写一个windows下的定时关机脚本(推荐)
Mar 21 Python
pandas将DataFrame的列变成行索引的方法
Apr 10 Python
python实现黑客字幕雨效果
Jun 21 Python
python opencv实现运动检测
Jul 10 Python
flask-restful使用总结
Dec 04 Python
python3 json数据格式的转换(dumps/loads的使用、dict to str/str to dict、json字符串/字典的相互转换)
Apr 01 Python
python实现连连看辅助(图像识别)
Mar 25 Python
Python 2种方法求某个范围内的所有素数(质数)
Jan 31 Python
详解python如何引用包package
Jun 07 Python
Python中常见的导入方式总结
May 06 Python
Python机器学习之基于Pytorch实现猫狗分类
Jun 08 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基础之运算符的使用方法
2013/04/28 PHP
PHP取二进制文件头快速判断文件类型的实现代码
2013/08/05 PHP
Laravel框架表单验证详解
2014/09/04 PHP
PHP中抽象类、接口的区别与选择分析
2016/03/29 PHP
js 对联广告、漂浮广告封装类(IE,FF,Opera,Safari,Chrome
2009/11/26 Javascript
Extjs学习笔记之五 一个小细节renderTo和applyTo的区别
2010/01/07 Javascript
JQuery中使用Ajax赋值给全局变量异常的解决方法
2014/01/10 Javascript
深入理解ECMAScript的几个关键语句
2016/06/01 Javascript
简单实现js选项卡切换效果
2017/02/09 Javascript
js实现适配不同的屏幕大小
2017/04/10 Javascript
基于JS实现移动端左滑删除功能
2017/07/28 Javascript
View.post() 不靠谱的地方你知道多少
2017/08/29 Javascript
浅谈vue中数据双向绑定的实现原理
2017/09/14 Javascript
JavaScript实现精美个性导航栏筋斗云效果
2017/10/29 Javascript
vue+webpack 打包文件 404 页面空白的解决方法
2018/02/28 Javascript
Vue2.0 v-for filter列表过滤功能的实现
2018/09/07 Javascript
7个好用的JavaScript技巧分享(译)
2019/05/07 Javascript
Electron vue的使用教程图文详解
2019/07/05 Javascript
iview的table组件自带的过滤器实现
2019/07/12 Javascript
解决Echarts 显示隐藏后宽度高度变小的问题
2020/07/19 Javascript
python模拟登陆阿里妈妈生成商品推广链接
2014/04/03 Python
用Python操作字符串之rindex()方法的使用
2015/05/19 Python
Python实现代码统计工具(终极篇)
2016/07/04 Python
Python解析json之ValueError: Expecting property name enclosed in double quotes: line 1 column 2(char 1)
2017/07/06 Python
使用pandas的DataFrame的plot方法绘制图像的实例
2018/05/24 Python
Python可视化mhd格式和raw格式的医学图像并保存的方法
2019/01/24 Python
在keras中实现查看其训练loss值
2020/06/16 Python
django 模型字段设置默认值代码
2020/07/15 Python
Python  Asyncio模块实现的生产消费者模型的方法
2021/03/01 Python
世界上最伟大的马产品:Equiderma
2020/01/07 全球购物
介绍一下EJB的体系结构
2012/08/01 面试题
小学生新学期寄语
2014/01/19 职场文书
总经理任命书
2014/03/29 职场文书
干部作风建设年活动剖析材料
2014/10/23 职场文书
汽车转让协议书范本
2014/12/07 职场文书
Java实战之课程信息管理系统的实现
2022/04/01 Java/Android