DNS C2

2022-01-16 20:31:56 浏览数 (1)

设置

使用以下步骤为 DNS C2(和 DNS Canaries)配置域,只要您正确设置记录,您就可以使用任何您希望的 DNS 提供商。我建议为每条记录设置约 5 分钟的 TTL。

  1. 为您指向您的 Sliver 服务器(或重定向器)IP 地址创建一条A记录。example.com
  2. 为指向您的 Sliver 服务器(或重定向器)IP 地址的子域(即)创建A记录。ns1ns1.example.com
  3. 创建一个具有任意子域的 NS 记录,例如1(即1.example.com)由ns1.example.com.
  4. 您现在可以1.example.com用作您的 DNS C2 域,例如generate --dns 1.example.com.(发出 DNS 命令时始终使用 FQDN)。

重要提示:在 Sliver 控制台中发出 DNS 命令时,始终使用 FQDN(例如,1.example.com.注意结尾的.)。DNS 是一个挑剔的协议!

域的最终配置应如下所示lil-peep.rip

重要提示:请记住在配置这些记录时禁用 Cloudflare 的“云”,并调整 TTL。

DNS 金丝雀

DNS Canaries 是唯一的二进制域,可以在字符串混淆过程中选择性地插入。这些域实际上并没有被植入代码使用,并且故意不进行混淆,以便在有人strings在植入上运行时显示出来。如果这些域被解析(并且您有一个dns正在运行的侦听器),您将收到一条警报,告知蓝队发现了哪个特定文件。

带有金丝雀的示例generate命令,请确保使用 FQDN:

代码语言:javascript复制
sliver > generate --http foobar.com --canary 1.example.com.

确保您有一个 DNS 侦听器正在运行,并使用 FQDN:

代码语言:javascript复制
sliver > dns --domains 1.example.com.

[*] Starting DNS listener with parent domain(s) [1.example.com.] ...
[*] Successfully started job #1

canaries您可以使用该命令查看以前生成的金丝雀。

Ubuntu

注意:在最新版本的 Ubuntu 上,您可能需要禁用systemd-resolved它,因为它会绑定到您的本地 UDP:53 并搞砸有关 DNS 应该如何工作的所有内容。要使用健全的 DNS 配置,请以 root 身份运行以下命令,因为resolved可能也损坏sudo了:

代码语言:javascript复制
systemctl disable systemd-resolved.service
systemctl stop systemd-resolved
rm -f /etc/resolv.conf
vim /etc/resolv.conf

添加一个正常的resolv.conf

代码语言:javascript复制
nameserver 1.1.1.1
nameserver 8.8.8.8

注意:这描述了 DNS C2 的 v1.5 实施。此外,我不打算介绍加密密钥交换,您可以在此处阅读,这只是关于我们如何来回移动字节。

设计目标

当前 DNS C2 的实现主要是为“速度”(就 DNS 隧道而言)而不是隐身而设计的;它并不打算在使用 DNS 来传输数据时变得微妙。虽然 DNS 可以是一个非常有用的隐蔽信令协议,但 Sliver 正在创建一个全双工隧道,秘密地这样做通常太慢而无法实用。一般的经验法则是,如果您查看DNS C2 很容易检测到。这并不是说 DNS C2 没有用或会立即被检测到,因为通常没有人看到。由于 DNS 不需要直接的“视线”网络,因此它通常可用于从高度受限的网络中通过隧道传输出去,如果环境没有专门检测 DNS C2,它很可能不会被检测到。

例如,忽略一些细节,如果 DNS 客户端尝试访问foo.1.example.com它,它将查询它的本地解析器以获取答案。如果解析器不知道答案,它将开始“递归”解析域,最终找到到达域的“权威名称服务器”的路径,该域由example.com. DNS C2 通过填充子域中的数据来工作,然后将对该子域的查询发送到权威名称服务器。

代码语言:javascript复制
[implant] <--> [resolver]
                  |<------> [.]
                  |<------> [.com.]
                  |<------> [example.com.] (attacker controlled)

这允许植入程序与攻击者控制的主机建立连接,即使植入程序无法将 TCP 或 UDP 流量路由到互联网。权衡是 DNS 查询通常非常小,事实上,如果我们幸运的话,我们可以为每个查询编码大约 175 个字节(稍后会详细介绍)。所以如果你想下载一个 2Mb 的文件,那么我们将不得不发送很多查询,这意味着即使在最好的情况下,DNS 通信也会很慢(~30Kbps)。

粗鲁的 DNS 解析器

新手黑客通常会认为像 DNS 这样的东西,作为互联网的基本组成部分,将被严格定义和实施。这当然与现实相去甚远。DNS 协议规范实际上更像是您所说的“指南”,而不是实际规则。DNS 解析器通常会忽略 TTL 值、修改查询内容、丢弃数据包以及在响应中撒谎。由于我们无法控制在操作环境中可能需要使用哪些解析器,并且我们希望构建可靠的 C2 连接,因此我们必须期待这种类型的行为并围绕它进行设计。

编码器选择

因此,如果我们想建立一个“快速”的 DNS 隧道,我们需要能够将尽可能多的数据打包到每个请求中,每个查询的数据越多,我们需要为给定消息发送的查询就越少。

那么我们可以在 DNS 查询中填充多少数据呢?好吧,让我们看一下 DNS 查询的内容,DNS 查询包含一些标头字段,然后是“问题”部分,单个查询可能包含多个问题。例如,我们可以询问“example.com 是否有 A 记录”(A 记录包含 IPv4 地址)。现在,我们可以将一些位编码到一些标头字段和其他几个部分,如类型/类,但到目前为止最大的字段是QNAME,它包含我们要询问的域:

代码语言:javascript复制
                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
     -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    |                                               |
    /                     QNAME                     /
    /                                               /
     -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    |                     QTYPE                     |
     -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    |                     QCLASS                    |
     -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

因此,我们将专注于将数据编码到QNAME(域)中,域的长度最多为 254 个字符,并且只能包含 ASCII a-zA-Z0-9-,但-不能出现在名称的开头,当然还有.分隔符。每个子域由 a 分隔,.最长可达 63 个字符。但是,DNS 是一个不区分大小写的协议,因此aADNS 的角度来看被认为是平等的。现在,正如我之前所说,规范实际上比规则更多的是指导方针,因此两者aA技术上都是允许的,它们在 DNS 语义中被认为是平等的。

现在我们要发送任意二进制数据,但 DNS 不允许二进制数据,QNAME所以我们需要将二进制数据编码成允许的字符集。通常当然要将任意二进制数据编码为 ASCII,我们会使用Base64编码,但 DNS 只允许 62 个不同的字符(a-z, A-Z, 0-9),因此不能使用 Base64。此外,我们必须考虑到 Base64a并且A是不同的值,而对于 DNS,它们不是。因此,如果某些粗鲁的解析器将我们其中一个QNAMEs 中的所有字符转换为小写,那么使用像 Base64 这样的区分大小写的编码可能并不总是安全的。就 DNS 而言,这很好,但它会破坏服务器解码的字节。

为了解决这个问题,许多 DNS C2 实现改为使用十六进制编码,它不区分大小写,并且只使用 a 中允许的字符 ( 0-9, A-F)QNAME从服务器来回传输数据。这个问题是十六进制是一种非常低效的编码,导致 x2 大小(编码一个字节需要两个字节),并且由于我们想要最小化我们需要发送的查询数量,这不是一个很好的选择。相反,我们可以使用Base32,它也不区分大小写,但使用更多字符来显示字节,因此在大约 x1.6 大小时效率更高。更好的是Base58,它像 Base64 一样区分大小写,但只使用 a 中允许的字符QNAME,消息大小略高于 x1.33,但如果遇到粗鲁的解析器,我们不能总是依赖能够使用 Base58。

Sliver 对这个问题的解决方案是首先尝试检测是否可以使用 Base58 来可靠地编码数据,如果检测到问题则回退到 Base32。我将此过程称为“指纹”解析器。

指纹粗鲁解析器

最常见的 DNS 查询是A请求与某个域名关联的 IPv4 地址的记录。IPv4 地址是一个 4 字节的值,通常在 Base10 中显示为四个八位字节192.168.1.1,但对于 DNS,这只是一个任意的 4 字节(32 位)值。

为了检测解析器是否损坏了我们消息中的任何字节,权威名称服务器将其接收到的数据的CRC32A编码到它接收到的任何记录的 IP 地址中。植入程序首先生成随机字节,然后对于主机上的每个解析器,我们尝试解析这些随机字节并检查服务器计算的 CRC32 是否与我们发送的数据的 CRC32 匹配。如果发生任何不匹配,则使用 Base32 而不是 Base58。

每个查询的字节数

由于用于发送数据的编码器是在运行时选择的,如上所述,可以编码到查询中的字节数取决于父域的长度和选择的编码器。因此,给定一些n字节消息,我们必须动态确定发送消息需要多少查询。

首先我们计算总域中有多少个字符可以用来编码数据,这取决于父域的长度。无论子域的数量如何,每个域始终最多应用 254 个字符。因此,例如,当使用1.abc.com(254 - 9 = 245) 与a.thisisalongdomainname.com(254 - 27 = 227) 时,我们有更多的剩余空间,但是我们还必须考虑.每 63 个字符,并且这些数量可能会根据之后剩余的空间而有所不同父域。可用于表示数据的字符数我称之为“子数据空间”(即不计算父域和.'s),计算方式为:

代码语言:javascript复制
254 - len(parent) - (1   (254-len(parent))/64)

这个“子数据空间”是我们的编码器(Base32 或 Base58)每条消息可以输出的最大字符数。所以每条消息的字节本质上是n字节编码长度必须等于或小于子数据空间。重要的是要注意,由于编码器效率低下,将单字节输入添加到 Base32 或 Base58 可能会导致 2 输出字符。

并行发送/接收

除了优化编码器的使用之外,如果我们可以无序发送带有编码数据的查询,或者也就是说并行发送查询,我们还可以提高性能,因为并行发送肯定会导致消息到达服务器命令。

为了说明这一点,Sliver 将n字节消息包装在包含一些元数据的 protobuf 消息中:

代码语言:javascript复制
message DNSMessage {
    DNSMessageType Type = 1; // An enum type
    uint32 ID = 2; // 8 bit message id   24 bit dns session ID
    uint32 Start = 3; // Bytes start at
    uint32 Stop = 4; // Bytes stop at
    uint32 Size = 5; // Total size
    bytes Data = 6; // Actual data
}

0 人点赞