本项目使用自定义的 NSEC3 参数生成 DNS 区域文件,以复现和评估 CVE-2023-50868 中的攻击。
Python3(已在 Python3.10 上测试)
已安装的 Python 依赖:
lib:Python 工具,包括:
keys.py:用于加载/存储密钥到文件的封装函数nsec.py:DNSSEC NSEC 哈希的实现dnssec.py:支持 NSEC3 的修改/修补版 dnspython 函数config.py:配置加载工具keys:包含预生成密钥的 PEM 文件(使用 gen_keys.py 生成)zones:区域文件(使用 gen_zones.py 生成)config.json:示例配置通过修改 config.json 来配置要创建哪些 NSEC3 区域(参见 配置)
生成密钥:
$ ./gen_keys.py
对于每个区域,都会生成一个 KSK 和一个 ZSK。 只要配置中的区域名称保持不变,更改配置时会重新使用这些密钥。
生成区域文件:
$ ./gen_zones.py -c
-c 选项启用配置文件的导出(目前仅支持 BIND9)
使用 --help 查看更多选项。
配置结构包含两个元素:
default:区域的默认参数(目前并非全部支持)zones:要导出的所有区域的列表一个区域包含:
name(必需):引用区域时使用的名称,也是导出时的文件名origin(必需):该区域的规范 origin 域名parent:父区域名称(不是 origin)。该区域的 NS、A、DS 和 NSEC3PARAM 记录会被添加到父区域中keysize(必需):RSA 密钥长度(目前仅支持 RSA)nsec3:NSEC3 参数:
iterations:默认为 0salt:默认为 ''algorithm:整数值,目前仅支持 SHA-1 (1)tight:一个特殊的布尔值,控制是否添加紧跟在 origin 之后以及 *.origin 之前和之后的 NSEC3 记录。例如,如果 *.origin 有一条 NSEC3 记录 1d..ua.origin.,那么 1d..u0.origin. 和 1d..ub.origin. 的记录也会被添加到区域文件中。这确保了 origin 的子域(例如 a.origin.)上的每个 NXDOMAIN 证明都需要三条 NSEC3 记录,因为覆盖 origin 和通配符的 NSEC3 记录到 next_hash 的范围非常小为了复现 NSEC3 攻击,本节展示了一个由 DNS 名称服务器和受害者解析器组成的自定义配置。 在继续之前,请确保系统环境已配置足够的防火墙,以免将公共服务器暴露于攻击区域文件。
安装 NSD 名称服务器(当前版本)
请访问 NLNetlabs 网站(https://nsd.docs.nlnetlabs.nl/en/latest/installation.html)查看安装说明。
建议在虚拟机或容器中部署名称服务器。作为起点,docker/nsd 中提供了一个小型 Dockerfile。
使用 docker build -t <tag> <path_to_dockerfile> 构建容器,例如:
cd docker/nsd && docker build -t nsd .
使用 docker run -it --name <name> nsd bash 运行容器,以在容器中打开控制台。
接下来,需要将名称服务器配置为托管攻击区域文件。
这需要正确配置要生成的区域文件(最重要的是,NS 记录中的 IP 地址需要与容器的 IP 地址匹配)。
如果未配置网络,可以通过以下命令查看容器的 IP 地址:
docker container inspect <name> | grep IPAddress
使用配置输出生成区域(./gen_zones.py -c,见上文),并将仓库目录中的 zones 输出文件夹复制到 docker 容器中:
docker cp ./zones <name>:/etc/nsd
在容器控制台中,需要将容器中的 NSD 配置 /etc/nsd/nsd.conf 追加以下内容:
verify:
enable: no
remote-control:
control-enable: no
include: "/etc/nsd/zones/nsd.conf"
最后,在容器 shell 中运行以下命令以启动 NSD:
/usr/sbin/nsd -d -c /etc/nsd/nsd.conf
如果您在按照本指南操作时遇到任何问题,请随时联系我们以获取进一步指导。
ns:该区域的名称服务器。可以是单个值或列表:
ns:名称服务器的域名,默认为 ns1.originip:IPv4 地址(目前不支持 IPv6),默认为 172.0.0.1soa:SOA RDATArrsets:附加 RRset 列表,以 5 元组列表 [domain name, ttl, class, type, rdata] 给出,其中所有值(ttl 可选除外)均为字符串使用 -V 4 选项启用日志输出。
现在,如果没有出现问题,权威名称服务器应该正在运行。您可以通过在主机系统上使用 dig 查询其中一个区域的域来验证这一点:
dig @<ip-addr-of-nsd-container> <domain>
安装解析器。在本演示中,我们展示一种适用于 Unbound 1.17.1 的可行方法。
官方 Dockerfile 可以在这里找到:https://github.com/NLnetLabs/pythonunbound
我们在 docker/unbound 中包含了一个修改版 Dockerfile,使用更新的 Ubuntu 版本,并预配置为 Unbound 1.17.1。
克隆仓库,进入其目录并构建 Unbound 容器:
docker build -t <tag> .
使用以下命令运行容器:
docker run --name <name> -it <tag> bash
接下来,需要配置 Unbound,使其能够找到 NSD 权威名称服务器。
可通过修改容器工作目录中的 unbound.conf 文件来完成。
为此,请确保从配置中移除 server.module-config 条目。
要启用 DNSSEC 验证,需要手动配置攻击者父区域的 DNSKEY 记录。 它必须与用于生成签名的密钥相同,例如:
server:
chroot: ""
do-ip6: no
trust-anchor: "attack.er. DNSKEY 257 3 7 AwEAAdqDN3rJYlmGP3jJs5lCZq5NYrCn pCVlV0ko17JnbfYfLCroEF4reO/Xy0MK C9AVvSRTk83MHDuzMYXogm7m/gcn3Mh0 MwB2InP8jkPw5not+TMH/Wrbs31xkT2n RIBJJ+1lPF+e2AvwWvgREcEVTRbdhIqQ iM1StWXoTVudry4V"
此外,还需要配置一个 stub-zone,使 Unbound 解析器能够找到 NSD 权威名称服务器。
可以通过在 unbound.conf 文件中添加以下内容来实现:
stub-zone:
name: "attack.er."
stub-prime: yes
stub-addr: <ip-addr-of-nsd-container>
在容器中启动 unbound:
unbound -vvv(使用 -dd 来防止守护进程化)
现在您应该能够使用 dig 查询 unbound 并观察响应时间:
dig @127.0.0.1 attack.er