DNSは「最後の砦」だ。RPZでマルウェアの足元をすくう現場の戦術
ネットワークエンジニアとして現場を渡り歩いていると、完璧なセキュリティなんて幻想だと痛感する瞬間が多々ある。どれほど強固なファイアウォールを築いても、どれほど最新のEDR(Endpoint Detection and Response)を導入しても、攻撃者は常にその「隙間」を縫ってくる。
だが、忘れないでほしい。どんなに巧妙な攻撃であっても、標的となった端末が外部のC2(Command & Control)サーバーと通信するためには、必ず「名前解決」という手続きを経る必要がある。
今日解説するのは、この「DNS」という通信の根幹を逆手に取り、マルウェアの通信を未然に遮断するDNS RPZ(Response Policy Zone)の実装術だ。教科書的な理屈ではなく、泥臭いトラブルシューティングで培った「現場で効く」知見を共有しよう。
—
DNS SinkholeとRPZの正体
DNS Sinkhole(DNSシンクホール)とは、本来解決されるべき悪意あるドメインのIPアドレスを、セキュリティ機器が管理する「偽のIP(Sinkhole IP)」にすり替える手法だ。これにより、マルウェアは攻撃者のサーバーではなく、ログを監視するための「観測用サーバー」へ誘導される。
このSinkholeを動的に、かつ大規模に運用するための仕組みが「RPZ」だ。BINDやUnboundといったDNSサーバーが、特定のゾーンファイルを「ポリシー」として読み込み、再帰的なクエリに対して「このドメインは怪しいから応答を書き換えるぞ」という判断をリアルタイムで行う。
通信フローの裏側を覗く
1. 感染端末: 「悪意あるドメイン」の名前解決を要求(Aレコードクエリ)。
2. DNSサーバー(RPZ有効): クエリを受信し、キャッシュまたは上位DNSへ問い合わせる前に、RPZテーブルをルックアップ。
3. マッチング: ドメインがリストに存在すれば、あらかじめ定義されたルール(CNAMEで別のドメインへ飛ばす等)を適用。
4. 結果: 端末は攻撃者のサーバーではなく、安全な「Sinkhole」のアドレスを受け取る。マルウェアの通信は、ここで完全に遮断される。
—
BINDでの実装:現場の「設定ファイル」
理論はこれくらいにして、実際にBINDでRPZを設定してみよう。設定の肝は、ゾーンファイルに rpz という役割を持たせることだ。
named.conf への定義
# RPZ用ゾーンの定義
zone "rpz.security.local" {
type master;
file "/etc/bind/db.rpz.security.local";
allow-query { none; }; # 再帰クエリを直接受け付けないように設定
};
options {
# 再帰クエリ時にRPZを参照するように設定
response-policy { zone "rpz.security.local"; };
};
ゾーンファイル db.rpz.security.local の記述
ここが攻撃者のドメインを封じ込める「檻」だ。
$TTL 60
@ IN SOA localhost. root.localhost. ( 1 60 60 60 60 )
IN NS localhost.
; C2サーバーのドメインをSinkholeへ誘導
; CNAMEで自分自身やダミーのIPへ飛ばすのが定石
malicious-c2-domain.com CNAME .
another-threat.net CNAME .
ここで CNAME . を指定すると、DNSクライアントは「そのドメインは存在しない(NXDOMAIN)」という応答を受け取る。攻撃者にとっては、DNSレベルで「通信が遮断された」という事実に気づきにくい、非常にスマートな拒絶方法だ。
—
現場のエンジニアへ:実装後のデバッグTips
RPZを導入した後、必ずぶち当たる壁がある。「誤検知(False Positive)」だ。業務システムが正規のAPI通信を行っている最中に、誤ってRPZが反応して通信が止まる。これだけは絶対に避けなければならない。
dig を使った検証手順
まずは、設定したドメインが正しくSinkholeへ誘導されているか、検証用端末から dig コマンドで確認しよう。
# 悪意あるドメインを問い合わせてみる
dig @127.0.0.1 malicious-c2-domain.com
# 正常に動作していれば、以下のように返ってくるはずだ
# ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 52345
もし意図通りに動かない場合は、tail -f /var/log/syslog(または named.run)を監視してほしい。BINDがRPZのゾーンファイルを読み込めていないか、文法ミスでゾーンを無視しているケースが多い。
API開発者が知るべきこと
Web APIの設計側としても、DNSレベルでの遮断が行われる可能性を考慮しておく必要がある。たとえば、Pythonで外部リソースを取得する際、DNS解決に失敗した場合の例外処理を丁寧に書くことだ。
import socket
import requests
def safe_request(url):
try:
# DNS解決を明示的に試みるなどのハンドリングを入れる
return requests.get(url, timeout=5)
except requests.exceptions.ConnectionError:
# DNS解決エラー(NXDOMAIN等)をキャッチしてログに残す
print(f"警告: DNSによる遮断または解決失敗を確認: {url}")
return None
—
最後に:ネットワークを「守る」という心構え
RPZは魔法ではない。攻撃者は DoH (DNS over HTTPS) を使って、あなたの管理するDNSサーバーをバイパスしようと躍起になっている。
しかし、DNSレベルでの検知は、ネットワーク管理者が「今、誰が、どの悪意あるドメインと繋がろうとしているのか」を把握できる数少ないチャンスだ。このログは、セキュリティ運用において宝の山になる。
設定して終わりではない。定期的にフィードバックリスト(脅威インテリジェンス)を更新し、誤検知がないかログを眺める。そんな地味な作業の積み重ねこそが、エンタープライズのネットワークを真に堅牢なものにするのだ。
現場の戦友たちよ。今日のDNS設定が、明日の大規模なランサムウェア被害を防ぐかもしれない。そう信じて、手を動かそう。
コメント