在本文中,我们报告了软件堆栈中的一系列缺陷,这些缺陷导致了DNS缓存中毒的强烈复活——这是一种经典的攻击,但在实践中通过简单有效的基于随机化的防御(如随机源端口)可以减轻这种攻击。要成功地毒害典型服务器上的DNS缓存,脱路对手将需要发送一个不切实际的232个欺骗响应,同时猜测正确的源端口(16位)和事务ID(16位)。令人惊讶的是,我们发现了允许对手通过先猜测源端口再猜测事务ID(导致只有216 + 216个欺骗响应)来“分而制之”空间的弱点。更糟糕的是,我们演示了对手可以扩展攻击窗口的许多方法,这大大提高了成功的几率。
该攻击影响DNS基础结构中的所有缓存层,如DNS转发器和解析器缓存,以及广泛的DNS软件栈,包括最流行的BIND、Unbound和dnsmasq,它们运行在Linux和其他操作系统之上。受害者容易受到攻击的主要条件是操作系统及其网络被配置为允许ICMP错误应答。
从我们的测量中,我们发现互联网上超过34%的开放解析器是脆弱的(特别是85%的流行DNS服务,包括谷歌的8.8.8.8)。此外,在受控实验和生产DNS解析器(授权)中,针对各种可能影响攻击成功的服务器配置和网络条件,我们全面验证了所提议的攻击,并获得了积极的结果。
两个关键步骤:
一旦知道了源端口号,攻击者只需通过爆破TxID的方式注入大量欺骗的DNS应答即可。这可以以高速完成,因为大多数服务器都有足够的网络带宽。
应用程序可以在一个UDP套接字上接收来自多个IP源地址的数据包。为了确保应用程序只从一个特定的源地址接收数据,这些应用程序必须在应用层实现相应的检查,或者显式地请求操作系统过滤接收到的数据包。
(1)当DNS服务器发出查询请求时,它的源端口就会对公众开放。因此攻击者可以使用UDP端口探测,如果没有探测到正确的端口时,服务器不会响应;或者只会返回一个ICMP端口不可达的信息
(2)如果套接字API允许在UDP套接字上使用connect()函数,那么当DNS查询从源端口发出到特定的目的IP地址和端口时,操作系统将只接受来自相同的远程IP和端口的传入数据包。如果远程IP错误或者端口号错误,则会响应一个ICMP端口不可达的信息。(防止源端口号被直接扫描)
总结一下:
源端口的可扫描性取决于DNS软件的实现,即是否在UDP套接字上发出connect() API调用
- 如果发出了connect() API调用,那么就无法直接扫描
- 如果没发出connect() API调用,那么就可以直接扫描
只有当以下两种情况同时满足,才能连接成功。否则会返回一个ICMP不可达的消息
- DNS响应报文的源IP与DNS查询报文的目的IP一致
- DNS响应报文的目的端口和DNS查询报文的源端口一致
发现在三种最流行的DNS转发器和解析器软件BIND、Unbound和dnsmasq中,只有BIND使用connect()。
ICMP速率限制
高效扫描UDP源端口的一个主要障碍是终端主机上传出ICMP错误消息的通常部署速率限制。
所以,即使在简单的情况下,(1)一个源端口是面向公共的,(2)可以被任何IP地址直接扫描,攻击者的扫描速度也受到每秒允许的ICMP报文数量的限制。
(1)Linux系统(主流的服务器操作系统)
IP速率限制:1个/s,每秒恢复一次
全局ICMP速率限制:1000个/s,每秒恢复1000次,是以每20ms恢复50次的方式进行。
(2)Windows Server 2019系统
全局ICMP速率限制:200个/s
(3)MacOS 10.15系统
全局ICMP速率限制:250个/s
(4)FreeBSD 12.1.0系统
全局ICMP速率限制:200个/s
面向公共源端口扫描方法
尽管在这种情况下,任何攻击者IP都可以直接探测源端口,例如unbound和dnsmasq。但为了实现更快的扫描速度,必须绕过每个IP速率限制(主要存在于Linux中)。我们开发了3种不同的探测方法,可以克服ICMP速率限制的挑战。
- 攻击者拥有多个IP地址,或者是多个bot机器
- 如果攻击者只有一个IPv4地址,仍然可以通过DHCP请求多个地址。我们验证了在一个家庭网络中可以获得多个私有IPv4地址。
- 利用IP欺骗绕过每个IP速率限制,并将全局速率限制作为一个侧通道来推断受欺骗的探针是否击中了正确的源端口,即是否有ICMP响应。
(1)首先攻击者发送50个欺骗的UDP探测报文,每个探测报文都有不同的源IP(绕过每个IP速率限制)
(2)如果受害服务器在这50个源端口中没有任何打开的源端口,那么将触发50个ICMP端口不可达消息(但攻击者无法直接观察到它们,因为ip是伪造的)
(3)如果受害服务器上有n个开放端口,则只会触发50-n个ICMP报文(因为这n个UDP探测报文会被应用层静默丢弃)。
(4)攻击者使用其真实IP地址发送验证报文,例如,发送到已知封闭端口的UDP报文,如1。它要么得不到响应(如果耗尽了全局速率限制),要么得到ICMP应答(没耗尽全局速率限制)
(5)如果得到应答,则说明这些端口里面存在一个开放端口,然后可以使用二分查找的方式从这50个端口里快速找到开放端口。
注意:前四步需要在一个20ms内完成。当50个猜测都没有击中正确端口时,我们需要发送“填充包”以确保全局速率限制被耗尽。填充报文是发送到已知的关闭UDP端口的欺骗报文,保证触发ICMP应答。(指上图右半边的情况,因为即使探测到了开放源端口,也不一定是DNS查询报文的源端口)
私有源端口扫描方式
如果connect()在UDP套接字上执行,则该端口有效地成为“私有”的远程对等体,使面向公共源端口扫描的方法无效。文中指出可以利用上游DNS服务器的源IP发送欺骗的UDP包。
我们可以用欺骗的权威名称服务器的IP发送探测不同源端口的UDP包。如果它击中正确的源端口,则不会生成ICMP应答。否则,就会有。然后,我们可以使用相同的全局ICMP速率限制作为侧通道来推断这样的ICMP消息是否已被触发。
但是该方法存在一个问题:那就是上游DNS服务器的源IP只有一个,我们无法通过伪造多个ip的方式来绕过ip速率限制,因此一秒只能发送一个UDP扫描报文。文中提出的解决方法是:
在分析ICMP速率限制实现的源代码之后,发现全局速率限制在每个ip速率限制之前被检查。这意味着,即使每个ip速率限制可能最终决定不应该发送ICMP应答,一个包仍然要接受全局速率限制检查,并扣除一个令牌。
因此我们完全可以使用上面的的方法进行探测,只是令牌是否被消耗殆尽,受害者服务器都只会返回一个ICMP响应报文。但这无影响。
有了这个前提,我们可以改进面向公共源端口扫描的方法中的第三个方法,只伪造一个ip即可。
考虑到上述攻击成功的要点在于:
- 需要DNS转发器或解析器支持全局ICMP速率限制
- 需要DNS转发器或解析器返回ICMP不可达响应报文
如果转发器或解析器可以被成功地推断出DNS查询的UDP源端口。
或者更具体地说,如果它支持全局ICMP速率限制,和/或如果它不使用connect()(使端口公开),则转发器或解析器被认为是脆弱的。
脆弱的DNS转发器
图中只有Verizon Fios Gateway(G1100)和Huawei A1(WS826)这两个路由器(一般会被当作DNS转发器)不会被认为是脆弱的DNS转发器。
(1)Verizon Fios Gateway(G1100):他们的Linux内核是旧版本,不支持全局ICMP速率限制
(2)Huawei A1(WS826):不返回ICMP不可达响应报文
脆弱的DNS解析器
与上表不同的是,有些DNS解析器会任意构造前端IP(例如,8.8.8.8)。而他们的后端ip才是我们可以扫描端口的可达服务器。多个这样的IP的存在增加了攻击的难度,因为我们需要决定扫描哪个IP。对于只有几个ip的情况,我们可以简单地同时扫描所有的ip。而对于OpenDNS、Ali DNS这两个DNS解析器会比较难。
攻击窗口:指DNS解析器向服务器发送查询,到权威域名服务器响应报文到达解析器的这段时间。
攻击窗口越长,攻击者可以扫描的端口就越多,注入恶意记录的时间也就越长。
主要想法是:“静默”上游服务器,防止它们能够响应DNS查询报文。并根据攻击目标(DNS转发器、DNS解析器)提出了两种策略。
(1)攻击者首先向转发器发送一个关于他自己的域(例如www.attacker.com)的查询,最终触发上游解析器查询攻击者控制的权威名称服务器。
(2)名称服务器被故意配置为无响应,以便转发器在保持源端口开放的状况下等待尽可能多的时间(因为解析器也停止了)。
DNS解析器
现代DNS名称服务器软件,如BIND、NSD、PowerDNS,都支持一种称为响应速率限制(RRL)的通用安全特性[55,59],以缓解DNS放大攻击[57],即欺骗受害者IP地址,向权威名称服务器发出大量恶意DNS查询。为了限制被放大的DNS应答报文的数量,RRL特性允许对触发的应答进行可配置的每个ip、每个前缀甚至全局限制。具体来说,如果达到了限制,则响应要么被截断,要么被删除。
如果攻击者能够以高于配置限制的速度注入欺骗的DNS查询(使用目标DNS解析器的IP),则可以恶意利用该特性来静音名称服务器。
测量方法:为了触发RRL,每秒发送1K个查询,持续15秒,然后再向每个域名服务器IP发送大约4kpps的测试,持续15秒;两个测试之间有两秒的间隔,以避免干扰。如果给定域有多个名称服务器,选择第一个。在这两种情况下,查询都是均匀分布的(而不是以突发方式发送),都试图询问WWW子域的记录。理由是1kpps和4kpps表示足够低的吞吐量,分别约为0.6Mbps和2.5Mbps。
结果分析:作者按照4kpps 进行测试,并按降序对丢失率进行排序,结果如图6所示。总体而言,大约有25%的域的名称服务器丢失率高于1%。
绕过缓存记录的TTL
解决方法:Kaminsky攻击
超时和重传查询
当在转发器或解析程序上触发DNS查询,并且没有从其上游接收到合法的响应时,它们不会永远等待。它们中的大多数都有一个超时时间来决定何时关闭当前套接字(以及相应的源端口)并重新传输。这意味着其中一些源端口可能是短暂的,很难捕获。
在没有失败的情况下,BIND和Unbound都维护一个默认的重传超时(RTO)—对于BIND来说是0.8s,对于Unbound则是一个基于RTT(到权威名称服务器)的动态计算值。
如果超时发生(例如,名称服务器没有响应或静音),如果有多个可用的名称服务器,它们将以轮询方式联系另一个名称服务器。如果所有的响应都失败了,它们将通过加倍RTO以指数方式后退——BIND只在连续3次失败后启动后退,而Unbound在每次失败后都启动后退)。
最后,还有另一个硬停止条件——BIND的默认总等待时间为10秒,Unbound的总等待时间为16到32秒(取决于查询的类型)。如果满足硬停止条件,将向客户端发送一个SERVFAIL。
这里我们将RTO称为“攻击窗口”,因为它表示源端口保持不变的持续时间。当窗口结束时,将选择一个不同的源端口,使之前的任何端口扫描过程无效。需要注意的是,当攻击窗口太小(例如,1s)时,即使正确识别了端口,注入64K流氓DNS记录仍然需要时间(在100kpps的泛洪速率下,仍可能需要几百毫秒),可能在窗口关闭前无法完成。
解决方法:上面提到的扩大攻击窗口的方式
处理多个权威名称服务器
这种方法也是一种常用的防DNS缓存投毒的方式。通过配置多个权威名称服务器ip,以实现冗余和安全性。
解决方法:一般策略是同时静音所有权威的名称服务器,因为它们的数量很少;还有一个方法是如果一个解析器是Unbound,它有一个独特的行为,如果它反反复复总是无法从最初联系的服务器听到消息,它将停止联系该名称服务器(将该服务器列入黑名单),并“永久”(这里的永久指的是时间很长,可能是分钟级别)切换到其他可用的服务器,
处理DNS解析器后面的多个后端服务器
发现后端服务器的数量通常严重倾向于几个(即使我们确实看到一些提供商总共有100多个),这可能是根据位置和过去的性能度量决定的。这允许我们同时只关注几个IP,考虑到每个IP只需要1kpps的扫描流量,这是很容易实现的。
通用解决方案:
通过附加的随机性和密码解决方案来缓解:DNSSEC、0x20编码、DNS cookie
针对该攻击的解决方案:
(1)源端口的猜测:
- 完全禁止传出的ICMP应答,代价是失去一些网络故障排除和诊断功能
- 采用随机化的ICMP全局速率限制
- 解析器在它们的UDP套接字上使用connect(),这样他们的源端口就不会是面向公共的,可以直接扫描
(2)延长攻击窗口:
- 更积极地设置DNS查询的超时时间(例如,始终低于1秒)。这样,源端口将是短暂的,并且在攻击者开始注入恶意响应之前消失。然而,缺点是可能引入更多重传查询和整体性能更差。
- 使用任播,使攻击者更难伪造受害者解析器ip对其使用的特定权威名称服务器进行DoS操作
实验设置
攻击者:Raspberry Pi(可以无线连接到路由器)
攻击目标:小米R3(一个Wi-Fi家庭路由器)
上行DNS服务器:CloudFlare DNS(1.1.1.1)。
网络拓扑:小米R3被用作唯一的网关,10到15个设备通过小米R3一直连接到互联网。
网络环境:在/24网络下,DHCP服务器默认配置为提供253个IPv4地址。
由于小米R3不支持全局ICMP速率限制(因此不需要使用侧信道的方式);而且它的转发器软件不调用UDP套接字上的connect函数(任何攻击者IP都可以直接探测源端口),所以使用攻击策略2来绕过每个ip的速率限制即可。为了扩展攻击窗口,使用针对DNS转发器的窗口扩展策略。
攻击过程
- 攻击者试图通过DHCP策略获取240个IP地址(绕过ip速率限制)
- 攻击者向转发器发出查询,请求任意子域,例如nonce.attacker.com。此时攻击窗口开始。
- 如果接收到SERVFAIL/NXDOMAIN,或者攻击者等待时间超过1分钟,表明发生了错误(即针对DNS转发器的窗口扩展策略失败了),我们将通过发出另一个查询来重复攻击过程。如果接收到NOERROR响应,则意味着成功注入了伪造响应。(确保攻击窗口被扩展)
- 攻击者使用获取的IP地址扫描路由器开放的端口。我们在可用的ip之间轮换(确保我们不会超过每个ip的速率限制)。
- 在发现一个端口是打开的之后,我们通过反复探测同一端口来确认它至少保持一秒钟的打开状态。(确认源端口没有因超时重传而更改)
- 如果是这样,我们就开始进行DNS缓存投毒。实验重复20次,我们报告成功率、平均成功时间和其他统计数据。
结果
总的来说,攻击是非常有效的,在20个实验中成功率为100%(如果攻击在30分钟内完成,我们就认为它是成功的)。
平均成功时间为271秒,第一阶段为103秒,第二阶段为168秒。第二阶段的标准差为109s,最大值为739s,最小值为83s。差异很大,因为攻击时间主要由攻击窗口大小决定,攻击窗口大小是解析器决定放弃并返回SERVFAIL/NXDOMAIN之前的超时时间。此外,攻击平均需要扫描36325个端口才能成功;端口扫描的平均速度为210pps,与使用240个ip进行扫描时的预期速率240pps大致吻合。此外,攻击产生的流量为78 MB。
实验设置
攻击者:在邻近的网络中的一台机器,距离解析器4跳,它有一个1Gbps以太网,可以执行IP欺骗。
攻击目标:合作者的DNS解析器
实验环境:设置了一个测试域,并将其托管在由我们控制的权威服务器上,以便我们只毒害我们自己的测试域。
实验时间:进行了20轮实验,一次在白天,另一次在当地时间午夜之后
攻击过程
- 攻击者生成查询nonce.attacker.com。
- 由于解析器有两个后端服务器ip,我们在两个ip上同时启动端口扫描。同时,我们以20pps的速度静音所有权威名称服务器的查询,以便解析器将经历80%的恒定丢失率。实验分别重复20次和5次为基线和改变。
- 先介绍分析现有防御措施的问题
- 然后提出针对这些防御方案的攻击措施并进行概述
- 针对攻击措施中的一些重要过程进行展开讲解
- 展开讲解时需要从以下几个方面展开:
重要过程的一些先验知识
执行这些流程时遇到的阻碍
如何绕过这些阻碍
重要过程的流程框架
对实际中的一些设备进行脆弱性评估
- 将论文中的内容应用到实际的时候会遇到的一些问题,以及缓解或解决办法
- 实验验证攻击的可行性:实验环境,实验过程,实验结果
- 对论文中攻击的一些特殊情况进行单独分析
- 防御措施