このプロジェクトは、カスタム 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: 設定例どの NSEC3 ゾーンを作成するかは、config.json を編集して設定します(設定 を参照)
鍵を生成:
$ ./gen_keys.py
各ゾーンについて、KSK と ZSK が生成されます。 ゾーン名が設定内で変更されていない限り、設定を変更しても鍵は再利用されます。
ゾーンファイルを生成:
$ ./gen_zones.py -c
オプション -c は設定ファイルのエクスポートを有効にします(現時点では BIND9 のみ対応)
詳細なオプションは --help を参照してください。
設定構造には2つの要素があります:
default: ゾーンのデフォルトパラメータ(現時点では一部のみ対応)zones: エクスポートするすべてのゾーンのリストゾーンには以下が含まれます:
name (必須): ゾーンを参照する際の名前。エクスポート時のファイル名にも使用されますorigin (必須): 正規のゾーンオリジンのドメイン名parent: このゾーンの NS、A、DS、NSEC3PARAM レコードが追加される親ゾーン名(origin ではありません)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 証明が3つの NSEC3 レコードを必要とするようになります。これは、origin とワイルドカードをカバーする NSEC3 レコードが next_hash に対して非常に狭い範囲を持つためですNSEC3 攻撃を再現するために、このセクションでは、DNS ネームサーバーと被害者リゾルバからなるカスタム環境の一例を示します。 続行する前に、攻撃用ゾーンファイルに対して公開サーバーが露出しないよう、システム環境のファイアウォールが適切に設定されていることを確認してください。
NSD ネームサーバー(現行バージョン)をインストール
インストール手順については、NLnetLabs のウェブサイト(https://nsd.docs.nlnetlabs.nl/en/latest/installation.html)を参照してください。
ネームサーバーは VM またはコンテナにデプロイすることを推奨します。出発点として、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、上記参照)、リポジトリディレクトリからゾーン出力フォルダを 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"
このガイドに関して問題が発生した場合は、お気軽にご連絡ください。さらなるサポートを提供します。
ns: このゾーンのネームサーバー。単一の値またはリスト:
ns: ネームサーバーのドメイン名。デフォルトは ns1.originip: IPv4 アドレス(IPv6 は現在サポートされていません)。デフォルトは 172.0.0.1soa: SOA RDATArrsets: 追加の RRset のリスト。5要素タプル [ドメイン名, ttl, class, type, rdata] として指定します。すべての値は(ttl は任意)文字列として指定します最後に、コンテナのシェルから次のコマンドで NSD を実行します:
/usr/sbin/nsd -d -c /etc/nsd/nsd.conf
ログ出力を有効にするには、オプション -V 4 を使用します。
ここまでで問題がなければ、権威ネームサーバーが実行されているはずです。ホストシステムから dig を使用して、いずれかのゾーンのドメインにクエリを実行することで確認できます:
dig @<ip-addr-of-nsd-container> <domain>
リゾルバをインストールします。このデモでは、Unbound 1.17.1 でのアプローチを示します。
公式 Dockerfile はこちらにあります: https://github.com/NLnetLabs/pythonunbound
この Dockerfile を修正したバージョンを docker/unbound に含めました。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"
さらに、Unbound リゾルバが NSD 権威ネームサーバーを見つけられるように、stub-zone を設定する必要があります。
これを行うには、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