mcpField
A
analyzing-dns-logs-for-exfiltration

analyzing-dns-logs-for-exfiltration

github.com/mukul975セキュリティ35.2k529 回インストール8 週間前に更新
リポジトリ

analyzing-dns-logs-for-exfiltration について

DNS トンネリング、DGA、データ流出の疑いを調査する SOC チーム向けのセキュリティ分析スキルです。Splunk クエリ、エントロピー分析、エンドポイント相関を使う手順を示します。

主な機能

  1. 01サブドメイン長とエントロピーの分析
  2. 02DNS クエリ量の異常検出
  3. 03TXT レコードの悪用検出
  4. 04既知のトンネリングツールの特徴照合
  5. 05エンドポイント相関と流出量の推定

活用シーン

  1. 01DNS トンネリングによる流出の調査
  2. 02DGA ドメインと DNS C2 の検出
  3. 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 と一緒にインストールされる参考資料やスクリプト。

LICENSE
references/
api-reference.md
scripts/
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、dns sourcetype、または 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

コメント