挖掘 DNS 层级结构的秘密,第一部分
我们的 DNS 正在工作。我们的客户端可以访问我们的网站、我们的安全电子商务服务、我们的 FTP 服务器和我们的 LDAP 服务。我们的人员可以发送电子邮件和浏览网页,我们的 VoIP 系统也完全正常运行。简而言之,所有在没有功能正常的 DNS 的情况下都无法工作的服务,似乎都在完全运行。我们的 DNS 一切都很好——也许吧。
在为我的第一本书进行研究时,我不断感到惊讶,而且不止一次地震惊,即使是大型的、技术精湛的组织,其 DNS 基础设施中也存在潜在的危险缺陷。本文介绍了一套简单的技术,使用两个现成的工具 dig 和 fpdns(参见“获取工具”侧边栏),有条不紊地探索 DNS 层级结构。所使用的技术是良性的;它们本质上是模拟缓存解析器的功能,可以用于诊断和审计目的。有一些工具可以涵盖这里展示的一些技术,但管理员必须理解整个过程,以避免工具中的任何缺陷。
尽管本文为了做一个好的网民,使用了不可避免的 example.com 和私有 IP 地址作为示例,但我鼓励您拉起一个 shell,并替换为您自己的域名,一个您感兴趣的、对您重要的或者您只是单纯好奇的域名。诊断示例显示了 BIND,因为它约占估计九百万台名称服务器的 80%。
为了使这些技术生动起来,让我们简单地查找我们目标域名 example.com 的网站的 IP 地址。首先,使用浏览器,访问该网站并四处浏览,点击有趣的链接,尤其是安全的链接。我们的目标是建立一个列表,列出我们目标使用的所有主机和域名。在我们的示例案例中,假设我们发现我们的目标使用 www.example.com 作为其公共门户,并使用第二个主机 online.example.com 用于安全 (HTTPS) 交易。从现在开始,我们只需模拟缓存 DNS 的行为,以找到通往目标网站的路径,并加入一些偏差来增加趣味性。
缓存名称服务器的主要工作是将域名(如 www.example.com)解析为 IP 地址。如果其缓存中没有关于该域名的信息,则必须跟踪从根服务器通过 DNS 名称层级结构到目标域名的权威名称服务器的委派路径。委派由父区域中的两个或多个名称服务器 (NS) 资源记录 (RR) 定义。父区域中的 NS RR 指向子区域的权威名称服务器。对于名称层级结构中的每个级别,都会重复此父子过程,直到我们到达目标。因此,对于 www.example.com,我们将从根域获得到 .com 权威名称服务器的委派,然后从 .com 获得到 example.com 权威名称服务器的委派。
缓存服务器从根区域或提示区域获取其初始根服务器列表,该区域使用通常称为 named.ca 或 named.root 的区域文件。要获取本地 DNS(在 /etc/resolv.conf 中定义)正在使用的根服务器的名称,只需输入dig.
您应该得到类似这样的响应
; <<>> DiG 9.4.1-P1 <<>> ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16298 ;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 7 ;; QUESTION SECTION: ;. IN NS ;; ANSWER SECTION: . 5058 IN NS A.ROOT-SERVERS.NET. ... . 5058 IN NS M.ROOT-SERVERS.NET. ;; ADDITIONAL SECTION: A.ROOT-SERVERS.NET. 798 IN A 198.41.0.4 ... M.ROOT-SERVERS.NET. 6957 IN A 202.12.27.33 ;; Query time: 36 msec ;; SERVER: 192.168.2.1#53(192.168.2.1) ...
为了简洁起见,我删除了上面很多内容,用 ... 序列表示。
dig 响应的格式有五个部分。“标头”包含摘要和状态信息,我们稍后会更详细地查看。“标头”之后的四个部分包含标准资源记录 (RR) 格式的信息,它们可能出现在区域文件中。“问题”部分反映了响应服务器收到的问题或查询。在上面的例子中,dig 命令被解释为“获取根域的 NS RR”。“答案”部分如果我们的问题没有得到回答,则可能为空;如果问题得到回答,则可能包含一个或多个 RR,这些 RR 是我们查询的答案。在上面的例子中,它包含根服务器(a.root-servers.net 到 m.root-servers.net)的 NS RR。特别注意此部分中每个结果行左侧臭名昭著的点,它是根域的简写形式。“权威”部分通常包含一个或多个 NS RR,用于对所查询域名具有权威性的服务器。在上面的例子中,它不存在,仅仅是因为“答案”部分已经包含了此信息。“附加”部分包含响应服务器认为可能有用且可用的任何信息。在本例中,以及在大多数情况下,它包含本地名称服务器使用的权威名称服务器的 A(地址)RR。
真正有趣的东西在“标头”中。首先要检查的是状态。在本例中,NOERR 表示命令成功(有关完整列表,请参见“Dig 标头值”侧边栏)。本例中的标志是 qr,表示我们收到了一个看起来非常合理的查询响应;rd,表示我们的 dig 消息请求了递归服务;以及 ra,表示此服务器支持递归服务(同样,有关可能的标志的完整列表,请参见“Dig 标头值”侧边栏)。“标头”还包含 id,它唯一标识此请求/响应对,最后总结了我们在每个部分中有多少 RR。
dig 响应的最后几行提供了有用的性能信息。“服务器”行尤其确认了从中获得结果的服务器的地址和名称。
现在,我们的工具包已经到位,我们对它告诉我们什么有了一些了解。因此,接下来,让我们沿着 DNS 层级结构从根域到我们的目标 www.example.com 和 online.example.com。
我们旅程中的第一个命令是
dig @a.root-servers.net www,example.com
此命令使用其中一个根服务器 (a.root-servers.net) 来获取 www.example.com 的 A RR。纯粹主义者会说,为了避免可能的损坏源,我们应该始终使用 @x.x.x.x(IP 地址)而不是名称,尤其是在诊断模式下,总的来说,这种方法更安全。但是,名称更易于理解(DNS 的原因),我们始终可以检查响应的“服务器”行上的 IP 地址是否在预期集合中。dig 响应将如下所示
; <<>> DiG 9.4.1-P1 <<>> dig @a.root-servers.net www,example.com ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15570 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 14 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;www.example.com. IN A ;; AUTHORITY SECTION: com. 172800 IN NS A.GTLD-SERVERS.NET. .... com 172800 IN NS M.GTLD-SERVERS.NET. ;; ADDITIONAL SECTION: A.GTLD-SERVERS.NET 172800 IN A 192.5.6.30 A.GTLD-SERVERS.NET. 172800 IN AAAA 2001:503:a83e::2:30 .... ;; Query time: 38 msec ;; SERVER: 198.41.0.4#53(198.41.0.4) ...
同样,为了简洁起见,我删除了很多内容,用 ... 序列表示。首先要注意的是标头中 ra 标志未设置,这意味着递归不可用——这在根服务器和 TLD 服务器中是正常的。其次,aa 标志未设置,这意味着这不是权威响应。乍一看,这可能看起来很奇怪;毕竟,这是一个根服务器。根域是 .com(层级结构中的下一个名称)的父域,但父域的 NS RR(委派点)永远不是权威的;只有子域才能为其 NS RR 提供权威响应。正如我们稍后将看到的,这具有重要的意义。总而言之,我们没有答案(ANSWER 0)且没有错误(状态 NOERR),但有“权威”(13) 条目。这会将响应标识为引用。根域无法提供答案,但很乐意将我们引用到层级结构中的下一层——在本例中,是 .com gTLD 服务器,其名称在“权威”部分中给出,一些 IP 地址在“附加”部分中给出,包括 IPv6 地址,这正变得越来越普遍。
继续我们的旅程,我们发出
dig @a.gtld-servers.net www.example.com
此命令表示,使用其中一个 .com gTLD 服务器 (a.gtld-servers.net),获取 www.example.com 的 IP 地址。这可能开始看起来有点乏味。毕竟,根服务器和 gTLD 服务器是由专业的组织运营的,那么为什么要费心检查它们呢?但是,请考虑,我们可能没有使用真正的根服务器和 gTLD 服务器。我们从本地缓存服务器获得了我们的列表。如果它的提示区域被污染了,此过程将帮助我们识别与正常情况的偏差,此外,一些 ccTLD 区域具有非常有趣的结构。黄金法则是不要做任何假设。dig 响应将如下所示
; <<>> DiG 9.4.1-P1 <<>> @a.gtld-servers.net www.example.com ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20018 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 1 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;www.example.com. IN A ;; AUTHORITY SECTION: example.com. 86400 IN NS ns2.example.com. example.com. 86400 IN NS ns1.example.net. ;; ADDITIONAL SECTION: ns2.example.com 172800 IN A 10.10.0.2 ;; Query time: 80 msec ;; SERVER: 192.228.28.9#53(192.228.28.9)
此响应与上一个响应几乎相同。它是对域名 example.com 的权威名称服务器的引用,这些服务器是 ns2.example.com(在同一域名或区域中)和 ns1.example.net(不在同一域名中)——称为区域外或域外名称服务器。如果我们选择使用 ns2.example.com,我们可以使用提供的 IP 地址;这是一个正常的“粘合”A 记录,是使委派工作所必需的。但是,如果我们想使用 ns1.example.net,我们必须通过从根服务器到 .net gTLD 服务器重新启动该过程来获取其 IP 地址。即使响应的“附加”部分中经常提供 IP 地址,我们也必须这样做。
不信任区域外 IP 地址的原因是什么?我们可能会污染我们的权威名称服务器空间并允许域名劫持。让我们来说明其中的危险。假设您友好的本地缓存名称服务器来到我的不相关域名,例如 www.example.org,并且在此域名的区域文件中,我有以下区域记录
$ORIGIN example.org. ... ; NS RR for the domain -- perfectly correct IN NS ns3.example.org. IN NS ns1.example.net. ... ; normal name server A RR ns3 IN A 192.168.2.4 ... ; out-of-zone A RR . potentially naughty ns1.example.net. IN A 192.168.2.4 ;a malicious server
如果缓存服务器信任 ns1.example.net(区域外服务器)的 A 记录,并在没有从其权威来源读取的情况下缓存它,则随后使用 ns1.example.net 解析 example.com 名称将使用位于 192.168.2.4 的名称服务器,在该服务器上,伪造的区域文件可能会将所有 example.com 流量重定向到恶意站点。因此,如果我们有一个区域外(或域外)名称服务器,即任何与目标域名空间不在同一域名空间中的服务器,则其 IP 地址在从权威来源获得之前不得信任。这意味着从根域进行跟踪。现代版本的 BIND 会丢弃所有区域外 A RR,并在加载区域时显示相应的日志消息。
在我们的手动审计过程中,我们可以忽略 ns1.example.net,但真正的缓存 DNS 不会。它将用于大约 50% 的查询。为了彻底审计我们 Web 地址的路由,我们必须检查“权威”部分中出现的所有 DNS 服务器。任何薄弱的 DNS 都可能导致一部分用户流量受到损害。名称服务器收到的查询百分比不应与用户数量混淆。来自大型 ISP 的单个查询可能会影响不成比例的用户数量,尤其是在以区域为中心的站点中。
情况可能会变得更糟。假设我们已经通过根服务器和 .net 的 gTLD 服务器跟踪了 ns1.example.net,并且我们得到了对我们最后一次 dig 的此响应
; <<>> DiG 9.4.1-P1 <<>> @a.gtld-servers.net ns1.example.net ... ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49319 ;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 2 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;ns1.example.net. IN A ;; AUTHORITY SECTION: example.net. 172800 IN NS ns1.example.net. example.net. 172800 IN NS ns4.example.org. ;; ADDITIONAL SECTION: ns1.example.net. 172800 IN A 192.168.2.2 ;; Query time: 61 msec ;; SERVER: 192.5.6.30#53(192.5.6.30) ...
我们有另一个域外名称服务器 (ns4.example.org),我们必须使用与之前完全相同的原则,通过根域到 .org TLD 服务器来跟踪它,而后者反过来也可能响应另一个域外服务器,依此类推。这些名称服务器中的每一个都在名称解析中发挥作用,并且它们中的任何一个都可能是薄弱环节。对有时称为“传递信任”的研究表明,在极端情况下,可能涉及 400 多个名称服务器来解析一个名称。如果您将一小部分 DNS 流量交给其他人,请确保他们及其所有 DNS 服务器都处于良好状态。
我们甚至可以通过错误的委派来创建 DNS 循环。这是一个简单的区域文件委派循环
$ORIGIN example.org. ... NS ns1.example.net. NS ns2.example.net. ... $ORIGIN example.net. ... NS ns1.example.org. NS ns2.example.org. ...
尽管即使是粗略的一瞥也会显示这两个域名是无法解析的,但现在假设它们被埋在多层间接和传递信任中,可能达到两层、三层、五层或更多层。调试可能会变得极其复杂。此外,假设我们在 example.net 的区域文件中添加一个区域内名称服务器。我们现在的问题是间歇性错误和超时(或访问速度慢)而不是简单的不可用——始终是一个更难诊断的问题。
到目前为止,我们已经经历了 DNS 层级结构的危险,但我们甚至还没有触及我们域名的权威名称服务器。在本文的第二部分中,我们将看看当我们进入用户领域时会发生什么。现在,那可能会变得非常可怕。
获取工具
Dig 是 BIND 的正常发行版提供的众多实用程序之一,可以从 www.isc.org 获取源代码形式,并且作为大多数 Linux 发行版的软件包广泛可用,并且在 BSD 的端口系统中。它也可以安装在 Windows 2000、XP 和 Server 2003 上,供那些在异构环境中工作的管理员使用。对于在 Windows 上偶尔使用,无需完全安装 BIND;只需解压缩 Windows 发行版 zip 文件,并将 dig.exe、libbind9.dll、libdns.dll、libisc.dll、libisccfg.dll 和 liblwres.dll 复制到合适的便携式介质上即可。
fpdns 是一个 Perl 脚本,由 DNS 领域的两位最聪明的人(Roy Arends 和 Jakob Shlyter)开发,可以从 www.rfc.se/fpdns、FreeBSD 中的端口集合以及使用get-apt install fpdns对于 Debian/Ubuntu 用户。
Dig 标头值
dig 响应标头值
id:请求者(提问者)提供的 16 位消息 ID,并由响应者(回答者)不变地反射回来。标识事务。范围 0 到 65535。
标志可以是以下一个或多个值
AA(权威答案):如果响应是从区域主服务器或从服务器接收到的,则设置。
TC:(截断):长度大于允许值,在所有截断的消息上设置,但最后一个消息除外。
RD(期望递归):在查询中设置,如果支持递归,则复制到响应中。
RA(可用递归):在响应中有效,如果设置,则表示递归查询支持可用。
AD(已验证数据),仅限 DNSSEC:表示数据已可靠地验证。
CD(禁用检查),仅限 DNSSEC:禁用接收服务器上的检查。
状态字段响应代码
0 = NOERR:无错误。
1 = FORMERR:格式错误——服务器无法解释查询。
2 = SERVFAIL:名称服务器问题或缺少信息。通常也以与 REFUSED 相同的含义返回。
3= NXDOMAIN 域名不存在:仅对权威名称服务器有意义。
4 = NOTIMPL:未实现。
5 = REFUSED:通常是出于策略原因,例如,区域传输请求。
资源
DNS 统计信息:dns.measurement-factory.com
BIND:www.isc.org
BIND 配置:www.zytrax.com/books/dns
fpdns:www.rfc.se/fpdns
Ron Aitchison 是 Pro DNS and BIND 的作者,并且最喜欢使用 dig 来揭示奇异的 DNS 配置。总有一天,很快,他会获得真正的生活。