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