本周,在ACM CCS 2020大会上,来自加州大学河滨分校和清华大学的研究人员宣布了一种针对域名系统(DNS)的新攻击,称为SAD DNS(侧通道攻击的DNS)。该攻击利用现代操作系统(如Linux)中网络堆栈的最新功能,使攻击者能够恢复一种经典的攻击类别:DNS缓存中毒。作为今年早些时候一项协同披露工作的一部分,研究人员联系了Cloudflare和其他主要的DNS提供商,我们很高兴地宣布,1.1.1.1 Public Resolver不再容易受到这种攻击。
在这篇文章中,我们将解释该漏洞是什么,它与之前的这类攻击有什么关系,我们已经采取了哪些缓解措施来保护我们的用户,以及该行业应该考虑的未来方向,以防止此类攻击在未来成为问题。
域名系统(DNS)使互联网用户无需记住长串数字就可以四处走动。通常被称为“互联网电话簿”的东西更像是一个有用的翻译系统,它将自然语言域名(如blog.cloudflare.com或gov.uk)翻译成互联网的本地语言:IP地址(如192.0.2.254或[2001:DB8::cf])。这种转换是在幕后进行的,因此用户只需要记住主机名,而不必陷入记住IP地址的泥潭。
DNS既是一种系统,也是一种协议。它指的是在网络上管理与命名相关的数据的计算机的分层系统,它指的是这些计算机用来相互交谈以交流有关命名的答案的语言。DNS协议由对应于问题和回答的消息对组成。每个DNS问题(查询)和答案(回复)都遵循标准格式,并包含一组参数,其中包含相关信息,如感兴趣的名称(如blog.cloudflare.com)和所需的响应记录类型(如A代表IPv4或AAAA代表IPv6)。
这些DNS消息使用传输协议在机器之间的网络上交换。最初,DNS使用UDP,这是一种简单的无状态协议,在该协议中,消息被赋予一组指示源端口和目的端口的元数据。最近,DNS已经适应了使用更复杂的传输协议,如TCP,甚至高级协议,如TLS或HTTPS,它们将加密和强身份验证结合在一起(参见Peter Wu关于DNS协议加密的博客文章)。
不过,最常见的消息交换传输协议是UDP,它具有快速、无处不在且不需要设置的优点。由于UDP是无状态的,因此对未完成查询的响应配对取决于两个主要因素:源地址和端口对以及DNS消息中的信息。鉴于UDP既是无状态的,也是未经身份验证的,任何人(不仅仅是接收者)都可以使用伪造的源地址和端口发送响应,这会带来一系列潜在问题。
由于传输层本质上是不可靠和不可信的,因此DNS协议设计了额外的机制来防止伪造响应。消息中的前两个字节构成了查询和响应中必须相同的消息或事务ID。当DNS客户端发送查询时,它会将ID设置为随机值,并期望响应中的值匹配。这种不可预测性在协议中引入了熵,这使得恶意方不太可能在没有首先看到查询的情况下构建有效的DNS回复。还有其他潜在的变量需要考虑,比如DNS查询名称和查询类型也用于配对查询和响应,但这些变量很容易猜测,并且不会引入额外的熵。
密切关注该图的人可能会注意到,该度量引入的熵量只有16位左右,这意味着要找到与给定查询匹配的答案的可能性不到十万种。稍后会详细介绍这一点。
DNS服务器主要分为以下几类:递归解析器(如1.1.1.1或8.8.8.8)、名称服务器(如DNS根服务器或Cloudflare权威DNS)。生态系统中也有一些元素充当“转发器”,如dnsmasq。在典型的DNS查找中,这些DNS服务器协同完成将指定域的IP地址传递给客户端的任务(客户端通常是存根解析器-操作系统中内置的简单解析器)。有关DNS生态系统的更多详细信息,请访问我们的学习网站。SAD DNS攻击的目标是递归解析程序和名称服务器之间的通信。
DNS中的每个参与者(客户端、解析器、名称服务器)使用DNS协议相互通信。大多数最新的DNS创新都围绕着将用户和递归解析器之间的传输升级为使用加密。升级解析器和权威服务器之间的传输协议稍微复杂一些,因为它需要新的发现机制来指示解析器何时使用(以及何时不使用)更安全的通道。除了一些例子,比如我们与Facebook合作,使用TLS上的DNS对递归到权威的流量进行加密,大多数这些交换仍然发生在UDP上。这是启用这种针对DNS的新攻击的核心问题,也是我们以前见过的。
在2008年前,递归解析器通常使用单个开放端口(通常是端口53)向权威名称服务器发送和接收消息。这使得猜测源端口变得微不足道,所以攻击者需要猜测的唯一变量就是16位消息ID。卡明斯基描述的攻击相对简单:每当递归解析器查询给定域的权威名称服务器时,攻击者就会向解析器发送约65,000个可能的消息ID中的部分或全部的DNS响应。如果具有正确消息ID的恶意答案在来自权威服务器的响应之前到达,则DNS缓存实际上是有毒的,只要DNS响应有效(称为TTL,即生存时间),就会返回攻击者选择的答案,而不是真正的答案。
对于流行的域名,解析程序每个TTL联系一次权威服务器(最短可达5分钟),因此有大量机会发动这种攻击。缓存DNS响应的转发器也容易受到此类攻击。
为了应对此攻击,DNS解析器开始对源端口进行随机化,并仔细检查缓存数据的安全等级。要毒害这些更新的解析器,伪造的响应不仅需要猜测消息ID,还需要猜测源端口,从而使猜测的次数从数万次增加到10亿次以上。这使得攻击实际上是不可行的。此外,IETF还发布了RFC5452,内容是如何加强DNS免受猜测攻击。
应该注意的是,此攻击不适用于DNSSEC签名的域,因为它们的答案是数字签名的。然而,即使现在是2020年,DNSSEC还远未普及。
避免猜测源端口号和消息ID的另一种方法是将DNS响应一分为二。与计算机安全领域的常见情况一样,当攻击者发现新功能时,旧的攻击又会变成新的攻击。2012年,巴伊兰大学(Bar Ilan University)的研究人员阿米尔·赫兹伯格(Amir Herzberg)和哈亚·舒尔曼(Haya Schulman)发现,远程攻击者有可能破坏源端口随机化提供的保护。这种新的攻击利用了UDP的另一个特性:碎片化。有关UDP碎片主题的入门读物,请查看我们之前由Marek Majkowski撰写的关于该主题的博客文章。
此攻击的关键在于,在DNS中毒攻击中需要猜测的所有随机性都集中在DNS消息(UDP报头和DNS报头)的开头。如果UDP响应数据包(有时称为数据报)被分成两个片段,前半部分包含消息ID和源端口,第二部分包含部分DNS响应,则攻击者需要做的就是伪造第二个片段,并确保假的第二个片段在真正的第二个片段到达解析程序之前到达解析程序。当数据报被分段时,每个分段都会被分配一个16位的ID(称为IP-ID),用于在连接的另一端对其进行重组。由于第二个片段只有IP-ID作为熵(同样,这也是这一领域的常见问题),因此利用相对较少数量的伪造数据包可以进行此攻击。此攻击的缺点是必须首先将响应分成碎片,并且必须仔细更改碎片以通过原始区段计数和UDP校验和。
在最初和后续文章中还讨论了一种强制两台远程服务器在攻击者控制的点上相互发送数据包的方法,从而使这种攻击更加可行。这些细节都在论文中,但归根结底,描述两个服务器之间的最大可传输单位(MTU)的控制机制--它决定了在哪个点上包被分割--可以通过伪造的UDP包来设置。
我们在去年的一篇博客文章中探讨了这种风险,当时我们介绍了我们的多路径DCV服务,该服务通过从多个有利位置进行DNS查询,降低了证书颁发环境中的这种风险。然而,随着DNS提供商采取行动消除对碎片DNS数据包的支持(这是2020年DNS旗帜日的主要目标之一),基于碎片的攻击被证明越来越不有效。
另一种阻止源端口随机化的方法是使用服务器的某些可测量属性,从而更容易猜测源端口。如果攻击者可以询问服务器正在将哪个端口号用于挂起的查询,这将使构建欺骗数据包变得容易得多。不存在这样的东西,但事实证明有一些足够接近的东西-攻击者可以发现哪些端口肯定是关闭的(从而避免了发送流量)。一种这样的机制是ICMP“端口不可达”消息。
假设目标收到发往其IP和某个端口的UDP数据报,该数据报要么被应用程序接受并静默丢弃,要么因为端口关闭而被拒绝。如果端口关闭,或者更重要的是,关闭了发送UDP数据报的IP地址,则目标将发回ICMP消息,通知攻击者该端口已关闭。了解这一点很方便,因为攻击者现在不必费心猜测此端口上挂起的消息ID并移动到其他端口。对服务器的一次扫描有效地将有效UDP响应的搜索空间从232(超过10亿)减少到217(大约10万),至少在理论上是这样。
这个把戏并不总是奏效的。许多解析器使用“连接的”UDP套接字而不是“开放的”UDP套接字在解析器和名称服务器之间交换消息。连接的套接字绑定到操作系统层上的对等地址和端口,这使得攻击者无法猜测在目标和受害者之间建立了哪些“连接的”UDP套接字,而且由于攻击者不是受害者,所以它不能直接观察探测的结果。
为了克服这个问题,研究人员发现了一个非常聪明的窍门:他们利用ICMP速率限制作为辅助通道来揭示给定端口是否打开。ICMP速率限制是作为一种安全功能引入的(考虑到这种攻击有点讽刺意味),目的是防止服务器在不知情的情况下被用作反射攻击的参与者。概括地说,它用于限制服务器在给定时间段内发送的ICMP响应的数量。假设攻击者想要扫描10,000个端口,并向配置了每秒50个ICMP速率限制的服务器发送10,000个UDP数据包,则只有前50个端口会收到ICMP“port unreacable”消息作为回应。
在记住数据安全的核心规则之一之前,速率限制似乎无伤大雅:不要让私人信息影响可公开测量的指标。ICMP速率限制违反了这一规则,因为速率限制器的行为可能会受到攻击者猜测某个“秘密”端口号是否开放的影响。
攻击者想知道目标是否有开放的端口,因此它从权威服务器向该端口发送伪造的UDP消息。如果端口打开,则不会发送ICMP回复,并且速率计数器保持不变。如果端口不可访问,则会发送ICMP回复(返回给权威服务器,而不是攻击者),并且速率会增加1。虽然攻击者看不到ICMP响应,但它已经影响了计数器。该计数器本身在服务器之外是未知的,但任何外部观察者都可以通过发送UDP数据包并等待回复来测量它是否达到了速率限制。如果返回ICMP“port unreacable”回复,则表示尚未达到速率限制。没有回复意味着已经达到了速率限制。这会向外部观察者泄露关于计数器的一丁点信息,最终足以泄露所谓的秘密信息(无论欺骗请求是否通过)。
具体地说,攻击的原理如下:攻击者向目标发送一串探测消息(大到足以触发速率限制),但带有伪造的受害者源地址。在被探测集合中没有打开的端口的情况下,目标将向受害者发回相同数量的ICMP“端口无法到达”响应,并触发传出ICMP消息的速率限制。攻击者现在可以从自己的地址发送额外的验证消息,并观察ICMP响应是否返回。如果是,则该集合中至少有一个端口打开,攻击者可以分割该集合并重试,或者通过将可疑的端口号插入一组已知的关闭端口来执行线性扫描。使用这种方法,攻击者可以缩小到开放端口,并尝试猜测消息ID,直到成功或放弃,类似于最初的卡明斯基攻击。
首先,必须发现目标IP或一组目标IP。在某些情况下,这可能是微不足道的-单个转发器,或者一组固定的IP,可以通过探测和观察攻击者控制的区域来发现,但如果目标IP被跨区域分区,则会更加困难,因为攻击者看不到解析器出口IP,除非她可以监控受害者域的通信量。
攻击还需要足够大的ICMP传出速率限制才能以合理的速度扫描。扫描速度非常关键,因为它必须在对受害者名称服务器的查询仍处于挂起状态时完成。由于扫描速度实际上是固定的,因此本文描述了一种通过触发受害者的响应速率限制(RRL)来潜在地延长机会窗口的方法,RRL是一种防止伪造的DNS查询泛滥的技术。如果受害者实现了RRL,而目标解析器没有通过TCP实现重试(对部署DNS速率限制的定量研究显示,大约16%的名称服务器实现了某种类型的RRL),这可能会奏效。
通常,繁忙的解析程序会有短暂的端口打开和关闭,这会给攻击者带来误报打开的端口,并为与被攻击的查询不同的待定查询打开端口。
我们已经实现了对1.1.1.1的额外缓解,以防止消息ID猜测-如果解析器检测到ID枚举尝试,它将停止接受任何更多的猜测,并切换到TCP。这减少了攻击者的尝试次数,即使它正确地猜测了IP地址和端口,这与密码登录尝试次数的限制类似。
归根结底,这些只是减刑,攻击者可能愿意玩长期游戏。只要传输层是不安全的,并且DNSSEC没有被广泛部署,就会有不同的方法来削弱这些缓解措施。
需要注意的是,试图隐藏源IP或开放端口号是一种通过隐藏来实现安全的方式。如果没有强大的加密身份验证,总是有可能使用欺骗来毒害DNS解析器。这里的一线希望是DNSSEC的存在,它的设计是为了防止这种类型的攻击,而且DNS服务器正在探索加密的强大传输,例如用于解析程序和权威服务器之间通信的TLS。
在Cloudflare,我们一直在帮助减少DNSSEC部署的摩擦,同时从长远来看也有助于提高运输安全。还有一项努力是使用RFC 7873-域名系统(DNS)Cookie增加DNS消息中的熵,并使基于TCP的DNS支持强制的RFC 7766-基于TCP的DNS传输-实现要求,并在不同的地方提供更多关于缓解此类问题的方法的文档。所有这些努力都是相辅相成的,这是一件好事。DNS生态系统由许多不同的各方和具有不同要求和观点的软件组成,只要运营商至少支持其中一种预防措施,这些类型的攻击就会变得越来越困难。
如果您是权威DNS服务器的操作员,则应考虑采取以下步骤来保护自己免受此攻击:
使用iptable阻止传出的ICMP“端口不可达”消息或降低Linux上的ICMP速率限制。
我们要感谢研究人员负责任地披露了这一攻击,并期待着在未来与他们合作,努力加强DNS。
DNS安全DNS毒化侧通道漏洞