analyzing-dns-logs-for-exfiltration について
DNS トンネリング、DGA、データ流出の疑いを調査する SOC チーム向けのセキュリティ分析スキルです。Splunk クエリ、エントロピー分析、エンドポイント相関を使う手順を示します。
主な機能
- 01サブドメイン長とエントロピーの分析
- 02DNS クエリ量の異常検出
- 03TXT レコードの悪用検出
- 04既知のトンネリングツールの特徴照合
- 05エンドポイント相関と流出量の推定
活用シーン
- 01DNS トンネリングによる流出の調査
- 02DGA ドメインと DNS C2 の検出
- 03ローカル DNS 監視を回避する DoH の発見
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill analyzing-dns-logs-for-exfiltrationインストール後、Claude Code で/analyzing-dns-logs-for-exfiltrationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
流出を目的とした DNS ログの分析
使用するタイミング
次の場合にこのスキルを使用します。
- SOC チームが、ファイアウォール/プロキシの制御を回避する DNS トンネリングによるデータ流出を疑っている
- 脅威インテリジェンスから、攻撃者が DNS ベースの C2 チャネル(例:Cobalt Strike の DNS ビーコン)を使用していることが示されている
- UEBA が、特定のホストからの異常な DNS クエリ量を検出した
- マルウェア分析で、DNS-over-HTTPS(DoH)または DNS トンネリング機能が明らかになった
標準的な DNS のトラブルシューティングや可用性監視には使用しないでください。このスキルは、セキュリティ上問題となる DNS の悪用検出に重点を置いています。
前提条件
- DNS クエリのログ記録が有効になっている(Windows DNS Server、Bind、Infoblox、または Cisco Umbrella)
- DNS ログが SIEM に取り込まれている(
Stream:DNSを使用した Splunk、dnssourcetype、または Zeek DNS ログ) - 過去のドメイン解決分析に利用できるパッシブ DNS データ
- 通常の DNS 動作のベースライン(クエリ量、ドメイン分布、TXT レコードの頻度)
- エントロピー計算に使用する
mathおよびcollectionsライブラリを含む Python
ワークフロー
手順 1:サブドメインの長さ分析による DNS トンネリングの検出
DNS トンネリングでは、サブドメインのラベルにデータをエンコードするため、異常に長いクエリが生成されます。
index=dns sourcetype="stream:dns" query_type IN ("A", "AAAA", "TXT", "CNAME", "MX")
| eval domain_parts = split(query, ".")
| eval subdomain = mvindex(domain_parts, 0, mvcount(domain_parts)-3)
| eval subdomain_str = mvjoin(subdomain, ".")
| eval subdomain_len = len(subdomain_str)
| eval tld = mvindex(domain_parts, -1)
| eval registered_domain = mvindex(domain_parts, -2).".".tld
| where subdomain_len > 50
| stats count AS queries, dc(query) AS unique_queries,
avg(subdomain_len) AS avg_subdomain_len,
max(subdomain_len) AS max_subdomain_len,
values(src_ip) AS sources
by registered_domain
| where queries > 20
| sort - avg_subdomain_len
| table registered_domain, queries, unique_queries, avg_subdomain_len, max_subdomain_len, sources
手順 2:エントロピーの高いドメインクエリの検出(DGA 検出)
ドメイン生成アルゴリズムは、ランダムに見えるドメインを生成します。
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval sld = mvindex(domain_parts, -2)
| eval sld_len = len(sld)
| eval char_count = sld_len
| eval vowels = len(replace(sld, "[^aeiou]", ""))
| eval consonants = len(replace(sld, "[^bcdfghjklmnpqrstvwxyz]", ""))
| eval digits = len(replace(sld, "[^0-9]", ""))
| eval vowel_ratio = if(char_count > 0, vowels / char_count, 0)
| eval digit_ratio = if(char_count > 0, digits / char_count, 0)
| where sld_len > 12 AND (vowel_ratio < 0.2 OR digit_ratio > 0.3)
| stats count AS queries, dc(query) AS unique_domains, values(src_ip) AS sources
by query
| where unique_domains > 10
| sort - queries
DNS クエリの Shannon エントロピーを Python で計算する方法:
import math
from collections import Counter
def shannon_entropy(text):
"""Calculate Shannon entropy of a string"""
if not text:
return 0
counter = Counter(text.lower())
length = len(text)
entropy = -sum(
(count / length) * math.log2(count / length)
for count in counter.values()
)
return round(entropy, 4)
# Test with examples
normal_domain = "google" # Low entropy
dga_domain = "x8kj2m9p4qw7n" # High entropy
tunnel_subdomain = "aGVsbG8gd29ybGQ.evil.com" # Base64 encoded data
print(f"Normal: {shannon_entropy(normal_domain)}") # ~2.25
print(f"DGA: {shannon_entropy(dga_domain)}") # ~3.70
print(f"Tunnel: {shannon_entropy(tunnel_subdomain)}") # ~3.50
# Threshold: entropy > 3.5 for subdomain = likely tunneling/DGA
エントロピースコアリングの Splunk 実装:
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval check_string = mvindex(domain_parts, 0)
| eval check_len = len(check_string)
| where check_len > 8
| eval chars = split(check_string, "")
| stats count AS total_chars, dc(chars) AS unique_chars by query, src_ip, check_string, check_len
| eval entropy_estimate = log(unique_chars, 2) * (unique_chars / check_len)
| where entropy_estimate > 3.5
| stats count AS high_entropy_queries, dc(query) AS unique_queries by src_ip
| where high_entropy_queries > 50
| sort - high_entropy_queries
手順 3:異常な DNS クエリ量の検出
異常な DNS トラフィックを生成しているホストを特定します。
index=dns sourcetype="stream:dns" earliest=-24h
| bin _time span=1h
| stats count AS queries, dc(query) AS unique_domains by src_ip, _time
| eventstats avg(queries) AS avg_queries, stdev(queries) AS stdev_queries by src_ip
| eval z_score = (queries - avg_queries) / stdev_queries
| where z_score > 3 OR queries > 5000
| sort - z_score
| table _time, src_ip, queries, unique_domains, avg_queries, z_score
TXT レコードの悪用(一般的なトンネリング手法)の検出:
index=dns sourcetype="stream:dns" query_type="TXT"
| stats count AS txt_queries, dc(query) AS unique_txt_domains,
values(query) AS domains by src_ip
| where txt_queries > 100
| eval suspicion = case(
txt_queries > 1000, "CRITICAL — Likely DNS tunneling",
txt_queries > 500, "HIGH — Possible DNS tunneling",
txt_queries > 100, "MEDIUM — Unusual TXT volume"
)
| sort - txt_queries
| table src_ip, txt_queries, unique_txt_domains, suspicion
手順 4:既知の DNS トンネリングツールの検出
一般的な DNS トンネリングツールのシグネチャを検索します。
index=dns sourcetype="stream:dns"
| eval query_lower = lower(query)
| where (
match(query_lower, "\.dnscat\.") OR
match(query_lower, "\.dns2tcp\.") OR
match(query_lower, "\.iodine\.") OR
match(query_lower, "\.dnscapy\.") OR
match(query_lower, "\.cobalt.*\.beacon") OR
query_type="NULL" OR
(query_type="TXT" AND len(query) > 100)
)
| stats count by src_ip, query, query_type
| sort - count
ローカル DNS を迂回する DNS over HTTPS(DoH)の検出:
index=proxy OR index=firewall
dest IN ("1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4",
"9.9.9.9", "149.112.112.112", "208.67.222.222")
dest_port=443
| stats sum(bytes_out) AS total_bytes, count AS connections by src_ip, dest
| where connections > 100 OR total_bytes > 10485760
| eval alert = "Possible DoH bypass — DNS queries sent over HTTPS to public resolver"
| sort - total_bytes
手順 5:DNS の調査結果とエンドポイントデータの関連付け
疑わしい DNS をプロセスデータと照合します。
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| stats count AS dns_queries, earliest(_time) AS first_query, latest(_time) AS last_query
by src_ip, query
| join src_ip [
search index=sysmon EventCode=3 DestinationPort=53 Computer="WORKSTATION-042"
| stats count AS connections, values(Image) AS processes by SourceIp
| rename SourceIp AS src_ip
]
| table src_ip, query, dns_queries, first_query, last_query, processes
手順 6:データ流出量の推定
DNS クエリにエンコードされたデータ量を推定します。
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| eval domain_parts = split(query, ".")
| eval encoded_data = mvindex(domain_parts, 0)
| eval encoded_bytes = len(encoded_data)
| eval decoded_bytes = encoded_bytes * 0.75 -- Base64 decoding factor
| stats sum(decoded_bytes) AS total_bytes_estimated, count AS total_queries,
earliest(_time) AS first_seen, latest(_time) AS last_seen
| eval estimated_kb = round(total_bytes_estimated / 1024, 1)
| eval estimated_mb = round(total_bytes_estimated / 1048576, 2)
| eval duration_hours = round((last_seen - first_seen) / 3600, 1)
| eval rate_kbps = round(estimated_kb / (duration_hours * 3600) * 8, 2)
| table total_queries, estimated_mb, duration_hours, rate_kbps, first_seen, last_seen
主な概念
| 用語 | 定義 |
|---|---|
| DNS トンネリング | DNS クエリ/レスポンス内にデータをエンコードし、データを流出させたり、DNS 経由で C2 チャネルを確立したりする手法 |
| DGA | ドメイン生成アルゴリズム。C2 の耐障害性を高めるために、疑似ランダムなドメイン名を生成するマルウェアの手法 |
| Shannon エントロピー | 文字列のランダム性を測定する数学的な指標。ドメイン名のエントロピーが高い(>3.5)場合、DGA またはトンネリングの兆候です |
| TXT レコードの悪用 | テキストデータ用に設計された DNS TXT レコードを、高帯域幅のデータトンネリングチャネルとして使用すること |
| DNS over HTTPS(DoH) | HTTPS(ポート 443)経由で暗号化された DNS クエリ。従来の DNS 監視を迂回します |
| パッシブ DNS | あるドメインが時間の経過とともにどの IP に解決されたかを示す、DNS 解決の履歴記録 |
ツールとシステム
- Splunk Stream:解析済みの DNS クエリデータを提供し、SIEM 分析に利用できるネットワークトラフィックキャプチャー用アドオン
- Zeek(Bro):分析用の詳細な DNS トランザクションログを生成するネットワークセキュリティ監視ツール
- Cisco Umbrella(OpenDNS):悪意のあるドメインをブロックし、クエリデータを記録するクラウド DNS セキュリティプラットフォーム
- Infoblox DNS Firewall:RPZ ベースのブロックと詳細なクエリログ記録を提供する DNS レイヤーのセキュリティ機能
- Farsight DNSDB:過去のドメイン解決の検索やインフラストラクチャのマッピングに使うパッシブ DNS データベース
一般的なシナリオ
- Cobalt Strike DNS ビーコン:C2 ドメイン宛ての、エンコードされたペイロードを含む周期的な TXT クエリを検出する
- データ流出:窃取したデータを Base64/hex でエンコードした、一意なサブドメインクエリの大量発生
- DGA マルウェア:アルゴリズムで生成されたドメイン(高エントロピーでウェブコンテンツがない)への DNS クエリを検出する
- DNS-over-HTTPS による迂回:従業員が DoH を使用して、企業の DNS フィルタリングと監視を迂回する
- 少量ずつの流出:しきい値アラートを下回るように DNS トンネリングの量を抑える(ベースラインとの比較が必要)
出力形式
DNS EXFILTRATION ANALYSIS — WORKSTATION-042
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Period: 2024-03-14 to 2024-03-15
Source: 192.168.1.105 (WORKSTATION-042, Finance Dept)
Findings:
[CRITICAL] DNS tunneling detected to evil-tunnel[.]com
Query Volume: 12,847 queries in 18 hours
Avg Subdomain Len: 63 characters (normal: <20)
Avg Entropy: 3.82 (threshold: 3.5)
Query Types: TXT (89%), A (11%)
Estimated Data: ~4.7 MB exfiltrated via DNS
Rate: 0.58 kbps (slow drip pattern)
[HIGH] DGA-like domains resolved
Unique DGA Domains: 247 domains resolved
Pattern: 15-char random alphanumeric.xyz TLD
Entropy Range: 3.6 - 4.1
Process Attribution:
Process: svchost_update.exe (masquerading — not legitimate svchost)
PID: 4892
Parent: explorer.exe
Hash: SHA256: a1b2c3d4... (VT: 34/72 malicious — Cobalt Strike beacon)
Containment:
[DONE] Host isolated via EDR
[DONE] Domain evil-tunnel[.]com added to DNS sinkhole
[DONE] Incident IR-2024-0448 created
コメント
Markdown 対応