iami233
iami233
文章175
标签37
分类4

文章分类

文章归档

浅析CobaltStrike流量解密

浅析CobaltStrike流量解密

Cobalt Strike 是一款GUI的框架式渗透工具,集成了端口转发、服务扫描,自动化溢出,多模式端口监听,win exe木马生成,win dll木马生成,java木马生成,office宏病毒生成,木马捆绑;钓鱼攻击包括:站点克隆,目标信息获取,java执行,浏览器自动攻击等等。

写在前面

目前突然感觉除了 Webshell (普遍为哥斯拉流量) 的流量分析外 CobaltStrike 的流量分析也多了起来,如 PHP-7-4-21-Development-Server 的源码泄露漏洞一般,当时看到此漏洞后就觉得要成为 CTF 考点了,后续也确实很多比赛考察了此漏洞。

另外以前只在 2021年绿城杯 流量分析 一题中接触并考察在已知私钥的情况下如何解密 CobaltStrike 的流量,但近期 某内部竞赛 以及 SICTF 等竞赛也出现了同类型题目,感觉有愈演愈烈之势,故在此记录一下

加密算法

Cobalt Strike 服务端和客户端是通过 SSL 加密通讯的 (默认证书对 cobaltstrike.store )。

Beacon 的元数据传输过程中虽然使用的是 RSA 算法,但是 Beacon 任务的传输使用的却是 AES 算法加密的,而 AES 密钥则是 Beacon 随机生成的然后通过 RSA 交换 AES密钥。加解密算法为 AES ,密钥位长 128CBC 模式,填充标准 PKCS7 ,其通信具体流程如下。

image-20230930201229567

流量传递

Cobalt Strike 分为 客户端服务端 ,服务端是一个,客户端可以有多个,可被团队进行分布式协作操作。

Cobalt StrikeBeacon 支持异步通信和交互式通信。Beacon 可以选择通过DNS还是HTTP协议出口网络,你甚至可以在使用Beacon 通讯过程中切换 HTTP 和 DNS 。其支持多主机连接,部署好 Beacon 后提交一个要连回的域名或主机的列表,Beacon 将通过这些主机轮询。

http-beacon 通信中,默认使用 GET 方法向 /dpixel/__utm.gif/pixel.gif 等地址发起请求,同时 Cobalt StrikeBeacon 会将元数据(例如AES密钥)使用 RSA 公钥加密后发送给 C2 服务器。这些元数据通常被编码为 Base64 字符串并作为 Cookie 发送。

1
2
3
4
5
6
7
8
9
10
11
12
http-get {

set uri "/news/pictures/animals/cat.jpg /ca /dpixel /__utm.gif /pixel.gif /g.pixel /dot.gif /updates.rss /fwlink /cm /cx /pixel /match /visit.js /load /push /ptj /j.ad /ga.js /en_US/all.js /activity /IE9CompatViewList.xml"; # 设置get请求涉及到的uri,get请求一般是心跳包,beacon会随机从里面找一个请求

client {
...
}

server {
...
}
}

image-20231001005213070

下发指令的时候会请求 /submit.php?id=一串数字 ,同时 POST 传递了一串 0000 开头的16进制数据,这是 cs 流量的发送任务数据

image-20231001004403837

https-beacon 通信中,默认使用空证书建立加密通道,流量中可以看见这一过程。

image-20230930233915

JA3 是由 John AlthouseJeff AtkinsonJosh Atkins 创建的开源项目。 JA3 / JA3S 可以为客户端和服务器之间的通信创建 SSL 指纹。唯一签名可以表示从 Client Hello 数据包中的字段收集的几个值:

  • SSL Version
  • Accepted Ciphers
  • List of Extensions
  • Elliptic Curves
  • Elliptic Curve Formats

这里列出几个已知的 ja3 / ja3s 指纹信息,这个值在不同操作系统上是不一样的

JA3

  • 72a589da586844d7f0818ce684948eea
  • a0e9f5d64349fb13191bc781f81f42e1

JA3s

  • b742b407517bac9536a77a7b0fee28e9
  • ae4edc6faf64d08308082ad26be60767

image-20231001100353522

流量解密

因为这里是拿 CTF 题目来讲的,所以必定会提供 .cobaltstrike.beacon_keys 文件,同时该文件本质上为 KeyPair 的 Java 对象,Python 的 javaobj-py3 库可以直接读取里面存储的数据。

1
2
$ file .cobaltstrike.beacon_keys 
.cobaltstrike.beacon_keys: Java serialization data, version

首先,我们需要先获取 .cobaltstrike.beacon_keys 文件中的私钥。这个私钥是 RSA 私钥,用于解密元数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
import javaobj.v2 as javaobj

with open(".cobaltstrike.beacon_keys", "rb") as fd:
pobj = javaobj.load(fd)

def format_key(key_data, key_type):
key_data = bytes(map(lambda x: x & 0xFF, key_data))
formatted_key = f"-----BEGIN {key_type} KEY-----\n"
formatted_key += base64.encodebytes(key_data).decode()
formatted_key += f"-----END {key_type} KEY-----"
return formatted_key

privateKey = format_key(pobj.array.value.privateKey.encoded.data, "PRIVATE")
publicKey = format_key(pobj.array.value.publicKey.encoded.data, "PUBLIC")

print(privateKey)
print(publicKey)

image-20231002121139358

然后我们在通过私钥解密元数据、获取 AES KEY,其中 encode_data 为元数据,也就是前面提到的 cookie 的值

Cobalt StrikeBeacon 通信主要依赖于 AES keyHMAC key 。这两个密钥都是由 Beacon 在每次执行时随机生成的 16字节数据。

AES key:这个密钥用于加密和解密 BeaconC2 服务器之间的通信内容。具体来说,它用于 AES 算法,该算法用于加密和解密Beacon任务的传输。

HMAC key :这个密钥用于验证数据的完整性和真实性。HMAC (Hash-based Message Authentication Code)是一种基于密钥的哈希算法,用于在不安全的通信环境中验证消息的完整性和真实性。

这两个密钥都是由同一组16字节数据生成的。具体来说,这组16字节数据的 SHA256 哈希的前半部分作为 HMAC 密钥,后半部分作为 AES 密钥。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import hashlib
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
import hexdump
PRIVATE_KEY = """-----BEGIN PRIVATE KEY-----
{}
-----END PRIVATE KEY-----"""

encode_data = ""

private_key = RSA.import_key(PRIVATE_KEY.encode())

cipher = PKCS1_v1_5.new(private_key)
ciphertext = cipher.decrypt(base64.b64decode(encode_data), 0)

if ciphertext[0:4] == b'\x00\x00\xBE\xEF':
raw_aes_keys = ciphertext[8:24]
raw_aes_hash256 = hashlib.sha256(raw_aes_keys).digest()
aes_key = raw_aes_hash256[0:16]
hmac_key = raw_aes_hash256[16:]

print("AES key: {}".format(aes_key.hex()))
print("HMAC key: {}".format(hmac_key.hex()))

hexdump.hexdump(ciphertext)

到此我们就得到了 AES keyHMAC key

image-20231002121301734

接着我们要去解密 submit.php 所传递的 Data 啦,首先我们要先对该串16进制数据进行处理,转字符串后进行 Base64 编码

1
2
3
4
5
6
7
8
import base64

encode_data = ''

bytes_data = bytes.fromhex(encode_data)
encrypt_data = base64.b64encode(bytes_data)

print(encrypt_data.decode())

最终分别填入 SHARED_KEYHMAC_KEYencrypt_data 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import hmac
import binascii
import base64
import hexdump
from Crypto.Cipher import AES

SHARED_KEY = binascii.unhexlify("")
HMAC_KEY = binascii.unhexlify("")
encrypt_data = ""

def decrypt(encrypted_data, iv_bytes, signature, shared_key, hmac_key):
if hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[:16] != signature:
print("message authentication failed")
return

cipher = AES.new(shared_key, AES.MODE_CBC, iv_bytes)
return cipher.decrypt(encrypted_data)

encrypt_data = base64.b64decode(encrypt_data)
encrypt_data_length = int.from_bytes(encrypt_data[:4], byteorder='big', signed=False)
encrypt_data_l = encrypt_data[4:]

data1 = encrypt_data_l[:encrypt_data_length-16]
signature = encrypt_data_l[encrypt_data_length-16:encrypt_data_length]
iv_bytes = b"abcdefghijklmnop"

dec = decrypt(data1, iv_bytes, signature, SHARED_KEY, HMAC_KEY)

print("counter: {}".format(int.from_bytes(dec[:4], byteorder='big', signed=False)))
print("任务返回长度: {}".format(int.from_bytes(dec[4:8], byteorder='big', signed=False)))
print("任务输出类型: {}".format(int.from_bytes(dec[8:12], byteorder='big', signed=False)))
print(dec[12:int.from_bytes(dec[4:8], byteorder='big', signed=False)])
print(hexdump.hexdump(dec))

image-20231002121511235

写在后面

这里提供三道练习题目,同时改了一下上面的脚本,更方便快速的得到想要的数据,项目地址:https://github.com/5ime/CS_Decrypt

  • 2021 绿城杯 流量分析
  • 2023 某内部竞赛 weblog
  • 2023 SICTF 一起上号不

参考文章

参考项目

本文作者:iami233
本文链接:https://5ime.cn/cobaltstrike-decrypt.html
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可