当前位置:   article > 正文

Cobalt Strike 的通信过程 & 协议_cobalt strike密钥

cobalt strike密钥

目录

通信流程

下载 Beacon

第一次心跳

 AES 密钥和 HMAC 密钥

获取下发命令

发送命令执行结果

扩展


这篇文章的主要内容是利用流量分析 Beacon 与 Cobalt Strike 服务端之间的通信过程,包括上线、心跳、命令下发与返回命令结果。当然,流量是加密的,需要一些辅助工具或脚本,其中的功能包括提取公钥/私钥数据包解构与字段分析等等。

通信流程

先从 payload 开始说起。

Cobalt Strike 提供了两种类型的 payload —— staged(有阶段的)stageless(无阶段的)有阶段是类似于小马拉大马的过程,先上传一个小马到受害者主机,执行后再通过这个小马下载功能更强大的大马去执行,而无阶段是直接上传大马去执行

比较下来,staged 比 stageless 多一次下载大马的过程,所以前者的通信过程要比后者的多一次请求-响应

这里以 staged payload 做描述,文件名为 beacon.exe,设置的监听器是 windows/beacon_http/reverse_http,即 HTTP 类型的 beacon。

通信的流程如下:

图中描述了 4 次交互过程,如果是 stageless payload 省略第一次请求-响应,换句话说,直接到上线的步骤。以下这 4 次交互过程进行说明:

1. 第一次请求时, beacon.exe 程序请求下载完整的 payload

2. 下好 payload 就直接在内存里运行,也就是说 beacon 开始运行,它做了如下操作:

(1)收集主机信息,生成用于协商的原始密钥 raw key,还有一些其他数据;

(2)用 RSA 公钥加密这些数据,将其存储在请求包的 cookie 中,然后发送给 CS 服务器。

这是第一次心跳,之后 beacon 进入睡眠时间。而CS 服务器收到后,做以下操作:

(1)用 RSA 私钥解开密文,从中提取主机信息和 raw key;

(2)在 target 列表生成一个新的主机;

(3)基于 raw key 生成 AES KEY HMAC KEY,用于后续的加密通信。

3. 睡眠时间过后,beacon 再次发送心跳包,这次发送的目的是询问 CS 服务器是否有命令下发

如果有,CS 服务器将包含命令的数据用 AES 加密得到密文,以及用 HMAC KEY 计算出 hash ,以 (AES 密文+hash) 的格式存放在 response  报文的响应体中。

如果没有,就跟第一次心跳一样,返回响应体为空的 response 报文。

4. 如果 beacon 收到命令,就在受害者主机上执行。执行完后,将命令执行结果发送给 CS 服务器,并重新进入睡眠时间

上述过程对应的流量:

下载 Beacon

双击 beacon.exe 第一次请求的流量:

请求 URI 是 4 个随机字符,请求它就会从 CS 服务器监听的端口下载 beacon,Response 里的数据就是 beacon。用 1768.py 解析其中的配置数据,并提取 RSA 公钥、CS 服务器的地址、监听器的端口等等:

这里 RSA 公钥是 16 进制,有些工具要求 base64 编码才能使用,这就要先转换成 2 进制,再用 base64 编码。这里用 python 完成这些步骤:

  1. import binascii
  2. import base64
  3. b = binascii.a2b_hex(b"30819f300d06092a864886f70d010101050003818d0030818902818100a5974fa8fe39957c54df8150faa30f2b61d98f8f0472108d89f899360a29e0dc2da506813f4a18e5976f55c1aad090688ee89abac2434f01fc8d0a9a5225b05ecb5b32c2a1dbc63b90714882e167202b1e62875bb98c000960eb145d70bbd431295d4ea07da029caef6fb625576cd96e638e816d6ed9f04d737550fbca95d841020301000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
  4. base64.b64encode(b)

 注意,后面一连串的 "AAA..." 是填充数据,要去掉的。

第一次心跳

下载的 beacon 直接在内存中运行起来,此时会发送第一个心跳包,用于上线:

 Cookie 里的数据包含了受害者主机的信息,这部分数据用了 RSA 公钥加密。如果我们想看看原始数据是什么样的,就需要对应的 RSA 私钥才能解开,但从攻击者的角度是没法拿到 RSA 私钥的。这里为了分析,用 Dump.java 程序从 .cobaltstrike.beacon_keys 提取私钥。

 注意,要把 Dump.java 放在 cs 根目录下,再执行命令: 

java -cp "cobaltstrike.jar" Dump.java

然后用私钥把 cookie 里的数据解开: 

 用 cs-decrypt-metadata.py 脚本也可以,不过要先把 RSA 私钥转换成 16 进制:

  1. import base64
  2. base64.b64decode("<RSA 私钥>").hex()

然后执行 cs-decrypt-metadata.py 解开数据:

python cs-decrypt-metadata.py -p <RSA 数字私钥> H0K9Mco+gUT3pTZyd5p13I1SYigQx3uSvk5tjIphuVfF/o4yyrhx4V0B00DwwrHVaavoCmpmcJEVC1jpslLuNwj7VwyJbD8IF82vmINNW2hA11uEVqfdDiTmI6R6Nl2vjB4Nkkft1hxUh3o0ophkho8I41Srgi4QehXUKlmGeq4=

解密结果:

第一部分是协商的密钥,后面会讲述如何利用 raw key 生成 AES 密钥和 HMAC 密钥的。第二部分是主机信息,包含主机名、用户名、beacon 进程名。

 AES 密钥和 HMAC 密钥

在讲述获取下发命令之前,先明白 AES 密钥和 HMAC 密钥是如何生成的,因为这两个密钥将用于后续 CS 和 beacon 之间的加密通信。上一节得到了 3 个 key:

  1. Raw key: 342ec70264b61f9749aa17558239eddb
  2. aeskey: e4e61a550d0cec57179ebbdb14ab6952
  3. hmackey: 6967ae5499b973f13e0b6bfc012cda27

AES 密钥和 HMAC 密钥是基于 raw key 生成的,使用的是 sha256 算法:

  1. # 将十六进制字符串转换成二进制 byte
  2. b = binascii.a2b_hex(b"342ec70264b61f9749aa17558239eddb")
  3. # 计算它的 sha256 摘要
  4. hashlib.sha256(b).digest().hex()

计算结果:

获取下发命令

每次睡眠时间结束,beacon 就会主动请求一次 CS 服务器,看看有没有下发命令。在 CS 客户端里执行 “shell whoami” 命令:

 就会抓取到这样的流量:

上面的 Request 报文是 beacon 发送的心跳包,与上线时第一次发送的基本一致。下面的 Response 报文返回了加密数据,这里面就有下发的命令。

加密数据分成两部分,第一部分是 AES 密文的,第二部分是 HMAC 签名,用于校验报文的完整性。密码算法是 CBC 模式 AES。

下面的脚本是我根据 cs-mitm.py 改写的:

  1. import binascii
  2. import Crypto.PublicKey.RSA
  3. import Crypto.Cipher.PKCS1_v1_5
  4. import Crypto.Cipher.AES
  5. import hashlib
  6. import hmac
  7. # 密码算法的初始向量
  8. CS_FIXED_IV = b'abcdefghijklmnop'
  9. def decrypt(aeskey, hmackey, data):
  10. # 密钥与密文
  11. aeskey = bytes.fromhex(aeskey)
  12. hmackey = bytes.fromhex(hmackey)
  13. data = bytes.fromhex(data)
  14. if aeskey == None:
  15. return data
  16. print("加密数据:", data.hex())
  17. # 截取 AES 密文
  18. encryptedData = data[:-16]
  19. print("AES 密文:", encryptedData.hex())
  20. # 截取加密数据的后 16 位用于校验密文的完整性
  21. hmacSignatureMessage = data[-16:]
  22. print("HMAC 签名:", hmacSignatureMessage.hex())
  23. # 校验密文的完整性
  24. hmacsSgnatureCalculated = hmac.new(hmackey, encryptedData, hashlib.sha256).digest()[:16]
  25. if hmacSignatureMessage != hmacsSgnatureCalculated:
  26. raise Exception('HMAC signature invalid')
  27. # AES 解密
  28. cypher = Crypto.Cipher.AES.new(aeskey, Crypto.Cipher.AES.MODE_CBC, CS_FIXED_IV)
  29. decryptedData = cypher.decrypt(encryptedData)
  30. return decryptedData
  31. data = decrypt("e4e61a550d0cec57179ebbdb14ab6952", "6967ae5499b973f13e0b6bfc012cda27", "e702026c696bf56ef54f1726a3db31c76fcd45ab045443fc711183859057acf82ee7687017ec95972f75c035f8c703f71a93aa3baa1457ccd1f01984ef054f24")
  32. print("AES 明文:", data)

 解密结果:

发送命令执行结果

当 beacon 接收到下发的命令并执行完成,将会把结果 POST 发送给 CS 服务器,流量如下:

 POST 请求体内包含了执行结果,也是用 AES 加密。同样地,payload 后 16 位字节是 HMAC 签名,不属于 AES 密文。此外,前 4 位字节也不属于 AES 密文。所以,这里只需要对前面的脚本修改一下,从第 4 个字节开始截取即可,这里就不粘贴代码了。

 解密结果:

 命令执行结果出现在明文中,解密成功。

扩展

Cobalt Strike Metadata Encryption and Decryption​​​​​​​

如何从进程转储文件提取 AES 密钥和 HMAC?Cobalt Strike: Using Process Memory To Decrypt Traffic – Part 3 – NVISO Labs

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/350512
推荐阅读
相关标签
  

闽ICP备14008679号