NVISO Labs - Cobalt Strike解密系列

【收集翻译】NVISO Labs

Cobalt Strike: Using Process Memory To Decrypt Traffic – Part 3

我们使用从进程内存中提取的加密密钥解密 Cobalt Strike 流量。
本系列博文描述了解密 Cobalt Strike 流量的不同方法。
本系列的第 1 部分中,我们揭示了在Cobalt Strike 软件包中发现的私有加密密钥。
第 2 部分中,我们从一个私有 RSA 密钥开始解密 Cobalt Strike 流量。
在这篇博文中,我们将解释如果您不知道私有 RSA 密钥但有进程内存转储,如何解密 Cobalt Strike 流量。

Cobalt Strike 网络流量可以使用正确的 AES 和 HMAC 密钥进行解密。在第 2 部分中,我们通过使用私有 RSA 密钥解密元数据来获得这些密钥。另一种获取 AES 和 HMAC 密钥的方法是从活动Beacon的进程内存中提取它们。
生成正在运行的Beacon的进程内存转储的一种方法是使用 Sysinternals 的工具procdump。不需要完整的进程内存转储,所有可写进程内存的转储就足够了。

生成可写进程内存的进程转储的命令示例:“procdump.exe -mp 1234”,其中 -mp 是转储可写进程内存的选项,1234 是正在运行的Beacon的进程 ID。进程转储存储在扩展名为 .dmp 的文件中。
对于 Cobalt Strike 版本 3 Beacon,未加密的元数据通常可以通过搜索字节序列 0x0000BEEF 在内存中找到。该序列是未加密元数据的标头。进程转储在进程的生命周期中越早进行,它就越有可能包含未加密的元数据。
image.png
图 1:进程内存中元数据的二进制编辑器视图
🛠️工具cs-extract-key.py🛠️可用于查找和解码此元数据,如下所示:
image.png
图 2:提取和解码的元数据
元数据包含原始密钥:16 个随机字节。AES 和 HMAC 密钥是通过计算原始密钥的 SHA256 值从该原始密钥派生的。SHA256 值的前半部分是 HMAC 密钥,后半部分是 AES 密钥。
然后,这些密钥可用于通过🛠️工具cs-parse-http-traffic.py🛠️解密捕获的网络流量,如第 2 部分所述

请注意,工具 cs-extract-key.py 可能会产生误报:即以 0x0000BEEF 开头的字节序列,但不是实际的元数据。图 2 中的示例就是这种情况:第一个实例确实是有效的元数据,因为它包含一个可识别的机器名称和用户名(查看字段:条目)。从该元数据中提取的 AES 和 HMAC 密钥也在进程内存的其他位置找到。但第二个实例的情况并非如此(没有可识别的名称,在其他位置找不到 AES 和 HMAC 密钥)。因此,这是一个必须忽略的误报。
对于 Cobalt Strike 版本 4 Beacon,很少能从进程内存中恢复未加密的元数据。对于这些Beacon,可以采用另一种方法。AES 和 HMAC 密钥可以在可写进程内存中找到,但没有标头清楚地标识这些密钥。它们只是 16 字节长的序列,没有任何可区分的特征。为了提取这些密钥,该方法包括执行一种字典攻击。在进程内存中找到的所有可能的 16 字节长的非空序列将用于尝试解密一段加密的 C2 通信。如果解密成功,则已找到有效密钥。
此方法确实需要进程内存转储和加密数据。
可以使用🛠️工具cs-parse-http-traffic.py🛠️提取此加密数据,如下所示:cs-parse-http-traffic.py -k unknown capture.pcapng
使用未知密钥(-k 未知),该工具将从捕获文件中提取加密数据,如下所示:
image.png
图 3:从捕获文件中提取加密数据
数据包 103 是对 GET 请求(数据包 97)的 HTTP 响应。此响应的加密数据长度为 64 个字节:

  1. d12c14aa698a6b85a8ed3c3c33774fe79acadd0e95fa88f45b66d8751682db734472b2c9c874ccc70afa40400000000000000000000

这是团队服务器发送到Beacon的加密数据:它包含要由Beacon执行的任务(请注意,在这些示例中,我们查看未转换的加密流量,我们将涵盖由可塑性指令转换的流量)即将发布的博客文章)。
我们可以尝试通过提供🛠️工具cs-extract-key.py🛠️与加密任务(-t选项)和进程内存转储解密此数据:

  1. cs-extract-key.py -t d12c14aa698a6b85a8ed3c3c33774fe79acadd0e95fa88f45b66d8751682db734472b2c9c874ccc70afa426fb2f510654df7042aa7d2384229518f26d1e044bd rundll32.exe_211028_205047.dmp

image.png
图 4:从进程内存中提取 AES 和 HMAC 密钥
然后可以使用恢复的 AES 和 HMAC 密钥来解密流量(-k HMACkey:AESkey):
image.png
图 5:使用通过选项 -k 提供的 HMAC 和 AES 密钥解密流量
图 5 中看到的解密任务是“数据抖动”。数据抖动是 Cobalt Strike 选项,它向Beacon发送随机数据(Beacon忽略的随机数据)。使用默认的 Cobalt Strike Beacon配置文件,不会发送随机数据,并且不会使用可延展指令转换数据。这意味着使用这样的Beacon配置文件,只要Beacon没有要执行的任务,就不会向Beacon发送任何数据:HTTP 回复的内容长度为 0。
由于没有任务会导致没有加密数据被传输,因此即使流量被加密,也很容易确定Beacon是否收到任务。缺少(加密)数据意味着没有发送任务。为了混淆命令(任务)的缺失,Cobalt Strike 可以配置为交换随机数据,使每个数据包都是唯一的。但在这种特殊情况下,随机数据对蓝队很有用:它允许我们从进程内存中恢复加密密钥。如果不会发送随机数据,也不会发送实际任务,我们将永远不会看到加密数据,因此我们将无法识别进程内存中的加密密钥。
Beacon发送给团队服务器的数据包含Beacon执行任务的结果。此数据通过 POST 请求(默认)发送,称为回调。该数据也可用于查找解密密钥。在这种情况下,过程与上面显示的相同,但使用的选项是 -c(回调)而不是 -t(任务)。选项不同的原因是团队服务器加密数据的方式与Beacon加密数据的方式略有不同,必须告诉工具使用哪种方式加密数据。

关于进程内存转储的一些注意事项

对于最大 10MB 的进程内存转储,“字典”攻击将需要几分钟。
也可以使用完整进程转储,但由于转储的大小较大,字典攻击可能需要更长的时间。🛠️工具 cs-extract-key.py 🛠️将进程内存转储读取为平面文件(Flat File是一种包含没有相对关系结构的记录的文件。这个类型通常用来描述文字处理、其他结构字符或标记被移除了的文本),因此更大的文件意味着要完成更多的处理。
但是,我们正在开发一种🛠️工具 cs-analyze-processdump.py🛠️,可以解析转储文件的数据结构并提取/解码最可能包含密钥的内存部分,从而加快密钥恢复过程。
请注意,Beacon可以配置为在它们不活动(休眠)时对其可写内存进行编码:在这种情况下,AES 和 HMAC 密钥也被编码,并且无法使用此处描述的方法恢复。我们正在开发的转储解析工具也将处理这种情况。
最后,如果此处针对第 3 版Beacon解释的方法不适用于您的特定内存转储,请尝试针对第 4 版Beacon的方法。此方法也适用于版本 3 Beacon。

结论

需要加密密钥来解密 Cobalt Strike 流量。最好的情况是有相应的RSA私钥。如果不是这种情况,可以使用进程内存转储和带有加密流量的捕获文件来恢复 HMAC 和 AES 密钥。