NVISO Labs - Cobalt Strike解密系列
Cobalt Strike: Decrypting DNS Traffic – Part 5
Cobalt Strike Beacon可以通过 DNS 进行通信。我们在这篇博文中展示了如何解码和解密 DNS 流量。
本系列博文描述了解密 Cobalt Strike 流量的不同方法。在本系列的第 1 部分中,我们揭示了在流氓 Cobalt Strike 软件包中发现的私有加密密钥。在第 2 部分中,我们从一个私有 RSA 密钥开始解密 Cobalt Strike 流量。在第 3 部分中,我们解释了如果您不知道私有 RSA 密钥但确实有进程内存转储,如何解密 Cobalt Strike 流量。在第 4 部分中,我们处理通过可塑性 C2 数据转换混淆的流量。
在本系列的前 4 部分中,我们一直着眼于 HTTP(或 HTTPS)上的流量。通过对 A、AAAA 和/或 TXT 记录执行 DNS 请求,还可以将Beacon配置为通过 DNS 进行通信。从Beacon流向团队服务器的数据用组成查询名称标签的十六进制数字进行编码,从团队服务器流向Beacon的数据包含在 A、AAAA 和/或 TXT 记录的答案中。
需要从 DNS 查询中提取数据,然后才能对其进行解密(使用与 HTTP 上的流量相同的加密方法)。
DNS C2协议
我们使用 2021 年版网络安全大战的挑战来说明 Cobalt Strike DNS 流量的样子。
首先,我们需要使用🛠️工具1768.py🛠️来查看Beacon配置:
图 1:DNS Beacon的配置
字段“payload type”确认这是一个 DNS Beacon,字段“server”告诉我们用于 DNS 查询的域:wallet[.]thedarkestside[.]org。
然后在图 1 中突出显示了第三个 DNS 配置参数块:maxdns、DNS_idle……当它们出现在我们要分析的 DNS 流量中时,我们将对其进行解释。
在 Wireshark 中看到,DNS 流量如下所示:
图 2:Cobalt Strike DNS 流量的 Wireshark 视图
我们将此信息(字段信息)浓缩为 DNS 查询和回复的文本表示:
图 3:Cobalt Strike DNS 流量的文本表示
让我们从第一组查询开始:
图 4:DNS_beacon 查询和回复
每隔一段时间(由睡眠设置决定),Beacon发出一个 A 记录 DNS 查询,名称为 19997cf2[.]wallet[.]thedarkestside[.]org。
wallet[.]thedarkestside[.]org 是此Beacon将发出的每个查询的根标签,这是在配置中设置的。19997cf2 是此特定Beacon实例的Beacon ID(bid)的十六进制表示。每个正在运行的Beacon生成一个 32 位数字,用于与团队服务器识别Beacon。每个运行的Beacon都是不同的,即使同一个Beacon可执行文件多次启动。针对此特定Beacon的所有 DNS 请求都将具有根标签 19997cf2[.]wallet[.]thedarkestside[.]org。
要确定上述一组 DNS 查询的目的,我们需要参考Beacon的配置:
图5:放大本Beacon配置的DNS设置(图1)
以下设置定义了每种查询类型的顶部标签:
- DNS_beacon
- DNS_A
- DNS_AAAA
- DNS_TXT
- DNS_元数据
- DNS_输出
请注意,图 5 中这些设置的值是默认的 Cobalt Strike 配置文件设置。
例如,如果此Beacon发出的 DNS 查询的名称以“http://www.”开头,然后我们知道这些是将元数据发送到团队服务器的查询。
在我们的beacon的配置中,DNS_beacon的值为(NULL …):这是一个空字符串,表示根标签前面没有放置标签。因此,通过这个,我们知道名称为 19997cf2[.]wallet[.]thedarkestside[.]org 的查询是 DNS_beacon 查询。
DNS_beacon 查询是Beacon用来查询团队服务器在其队列中是否有该Beacon的任务。对这个 A 记录 DNS 查询的回复是一个 IPv4 地址,该地址指示Beacon做什么。要了解指令是什么,我们首先需要将这个回复的地址与设置 DNS_Idle 的值进行异或。在我们的Beacon中,DNS_Idle 值为 8.8.4.4(默认 DNS_Idle 值为 0.0.0.0)。
查看图 4,我们看到对第一个请求的回复是 8.8.4.4。这些必须与 DNS_Idle 值 8.8.4.4 进行异或:因此结果为 0.0.0.0。等于 0.0.0.0 的回复意味着团队服务器队列中没有此Beacon的任务,它应该休眠并稍后再次检查。因此,对于图 4 中的前 5 个查询,Beacon无需执行任何操作。
这随着第 6 个查询而改变:回复是 IPv4 地址 8.8.4.246,当我们将该值与 8.8.4.4 进行异或时,我们最终得到 0.0.0.242。值 0.0.0.242 指示Beacon使用 TXT 记录查询检查任务。
以下是确定Beacon应如何与团队服务器交互的可能值:
图 6:可能的 DNS_Beacon 回复
如果设置了最低有效位,则Beacon应该进行签入(使用 DNS_metadata 查询)。
如果位 4 到 2 被清除,则应使用 A 记录进行通信。
如果设置了第 2 位,则应使用 TXT 记录进行通信。
如果设置了第 3 位,则应使用 AAAA 记录进行通信。
值 242 是 11110010,因此无需执行签入,但应通过 TXT 记录检索任务。
下一组 DNS 查询由Beacon执行,因为它收到了指令 (0.0.0.242):
图 7:DNS_TXT 查询
请注意,这些查询中的名称以 api. 开头,因此根据配置,它们是 DNS_TXT 查询(参见图 5)。这是根据团队服务器(0.0.0.242)的指令。
尽管 DNS_TXT 查询应该使用 TXT 记录,但 DNS_TXT 查询的第一个 DNS 查询是 A 记录查询。回复,一个 IPv4 地址,必须与 DNS_Idle 值进行异或。所以在我们的例子中,8.8.4.68 与 8.8.4.4 异或得到 0.0.0.64。这指定将通过 TXT 记录传输的加密数据的长度(64 字节)。请注意,对于 DNS_A 和 DNS_AAAA 查询,第一个查询也将是 A 记录查询。它还对要接收的加密数据的长度进行编码。
接下来,Beacon根据需要发出尽可能多的 TXT 记录查询。每个 TXT 记录的值是一个 BASE64 字符串,必须在解码之前连接在一起。一旦解码数据达到 A 记录回复中指定的长度(在我们的示例中为 64 字节),Beacon就会停止发出 TXT 记录请求。
由于Beacon可以非常快速地发出这些 TXT 记录查询(取决于睡眠设置),因此引入了一种机制来避免缓存的 DNS 结果会干扰通信。这是通过使 DNS 查询中的每个名称唯一来完成的。这是通过一个额外的十六进制标签完成的。
请注意,顶部标签(在我们的示例中为 api)和根标签(在我们的示例中为 19997cf2[.]wallet[.]thedarkestside[.]org)之间有一个十六进制标签。该十六进制标签对于第一个 DNS 查询是 07311917,对于第二个 DNS 查询是 17311917。该十六进制标签由一个计数器和一个随机数组成:COUNTER + RANDOMNUMBER。
在我们的例子中,随机数是 7311917,计数器总是从 0 开始并以 1 递增。这就是每个查询唯一的方式,它也有助于以正确的顺序处理回复,以防 DNS 回复无序到达。
因此,当收到所有 DNS TXT 回复时(在我们的示例中只有一个),base 64 字符串(ZUZBozZmBi10KvISBcqS0nxp32b7h6WxUBw4n70cOLP13eN7PgcnUVOWdO+tDCbeElzdrp0b0N5DIEhB 将在我们的示例中解密并解密)这篇博文)。
这就是 DNS Beacon从团队服务器接收指令(任务)的方式。加密字节通过 DNS A、DNS AAAA 或 DNS TXT 记录回复传输。
当必须通过 DNS A 记录(0.0.0.240 回复)进行通信时,流量如下所示:
图 8:DNS_A 查询
“cdn.”是 DNS_A 请求的顶部标签(参见配置图 5)。
第一个回复是 8.8.4.116,与 8.8.4.4 异或,得到 0.0.0.112。因此必须接收 112 字节的加密数据。:即 112 / 4 = 28 个 DNS A 记录回复。
加密数据仅取自 DNS A 记录回复中的 IPv4 地址。在我们的示例中,它是:19、64、240、89、241、225、…
而对于 DNS_AAAA 查询,方法完全一样,只是最上面的标签是 www6。在我们的示例中(参见配置图 5),并且每个 IPv6 地址包含 16 个字节的加密数据。
从团队服务器通过 DNS 记录传输到Beacon(例如任务)的加密数据与通过 http 或 https 传输的加密任务具有完全相同的格式。因此解密过程完全相同。
当Beacon必须将其结果(任务的输出)传输到团队服务器时,使用 DNS_output 查询。在我们的示例中,这些查询从顶部标签帖子开始。下面是一个例子:
图 9:使用 DNS_output 查询将结果发送到团队服务器的Beacon
DNS_output 查询的每个 DNS 查询名称都有一个唯一的十六进制计数器,就像 DNS_A、DNS_AAAA 和 DNS_TXT 查询一样。要传输的数据使用添加到名称的标签中的十六进制数字进行编码。
让我们进行第一个 DNS 查询(图 9):post.140.09842910.19997cf2[.]wallet[.]thedarkestside.org。
此名称分解为以下标签:
- post:DNS_output 查询
- 140:传输数据
- 09842910:计数器+随机数
- 19997cf2:Beacon ID
- wallet[.]thedarkestside.org:运营商选择的域名
第一个查询的传输数据实际上就是要传输的加密数据的长度。它必须按如下方式解码:140 -> 1 40。
第一个十六进制数字(在我们的示例中为 1)是一个计数器,用于指定用于包含十六进制数据的标签数量。由于 DNS 标签限制为 63 个字符,因此当需要编码 32 个或更多字节时,需要使用多个标签。这解释了计数器的使用。40 是十六进制数据,因此加密数据的长度为 64 字节长。
所述第二DNS查询(图9)为post.2942880f933a45cf2d048b0c14917493df0cd10a0de26ea103d0eb1b3.4adf28c63a97deb5cbe4e20b26902d1ef427957323967835f7d18a42.19842910.19997cf2[.]wallet[.]thedarkestside[.]org.
此查询中的名称包含用标签内的十六进制数字编码的加密数据(部分)。
这些是传输的数据标签: 2942880f933a45cf2d048b0c14917493df0cd10a0de26ea103d0eb1b3.4adf28c63a97deb5cbe4e20b26902d1ef427965783a8237967382
第一个数字 2 表示使用了 2 个标签来对加密数据进行编码:942880f933a45cf2d048b0c14917493df0cd10a0de26ea103d0eb1b3 和 4adf28c63a97deb5cbe4e209d7353209d7373737320737373497493df0cd10a0de26ea103d0eb1b3
第三个 DNS 查询(图 9)是:post.1debfa06ab4786477.29842910.19997cf2[.]wallet[.]thedarkestside[.]org。
标签的计数器为 1,传输的数据为 debfa06ab4786477。
将所有这些标签按正确的顺序放在一起,得到以下十六进制数据:
942880f933a45cf2d048b0c14917493df0cd10a0de26ea103d0eb1b34adf28c63a97deb5cbe4e20b26902d1ef4279573239671836a7d4b7d4b7d4。
那是 128 个十六进制数字长,或 64 个字节,与第一个查询中的长度(40 个十六进制)所指定的完全一样。
上面的十六进制数据,是从beacon通过DNS记录传输到团队服务器的加密数据(例如任务结果或输出),它的格式几乎与通过http或https传输的加密输出相同。
区别如下:对于 http 或 https 流量,格式以未加密的大小字段(加密数据的大小)开头。该大小字段不存在于 DNS_output 数据的格式中。
解密
我们开发了一个🛠️工具cs-parse-traffic🛠️,它可以解密和解析 DNS 流量和 HTTP(S)。与我们对加密 HTTP 流量所做的类似,我们将从 DNS 查询中解码加密数据,使用它在Beacon进程内存中查找加密密钥,然后解密 DNS 流量。
首先,我们使用未知密钥 (-k unknown) 运行该工具,以从捕获文件中的 DNS 查询和回复中提取加密数据:
图 10:从 DNS 查询中提取加密数据
需要选项 -f dns 来处理 DNS 流量,以及选项 -i 8.8.4.4。用于提供 DNS_Idle 值。需要此值才能正确解码 DNS 回复(DNS 查询不需要它)。
然后可以使用加密数据(红色矩形)在运行Beacon的进程内存转储中找到 AES 和 HMAC 密钥:
图 11:从进程内存中提取加密密钥
然后可以使用该密钥来解密 DNS 流量:
图 12:解密 DNS 流量
此流量用于Cyber Security Rumble 2021的 CTF 挑战。要找到该标志,请在解密的流量中 grep CSR:
图 13:在解密流量中找到标志
结论
DNS Cobalt Strike 流量和 HTTP Cobalt Strike 流量之间的主要区别在于加密数据的编码方式。一旦加密数据被恢复,解密它与 DNS 和 HTTP 非常相似。