【入門編】 DNSクエリログによるC2通信の検知 – インシデントレスポンス & フォレンジック(DFIR)防御ガイド

こんにちは!SOC(セキュリティ・オペレーション・センター)で日夜インシデントの調査を行っているアナリストです。

突然ですが、皆さんは「家の鍵」をしっかり閉めて出かけていますよね。では、「家の中の郵便受けから、泥棒がこっそり外へ向けて手紙を出していないか」を気にしたことはありますか?

実は、サイバー攻撃の世界でもこれとまったく同じことが起きているんです。会社のパソコンが悪い奴らに乗っ取られたとき、彼らは「外の仲間(C2サーバー)」と連絡を取るために、普段私たちがウェブサイトを見るために使っている「DNS」という仕組みをこっそり悪用します。

今回は、セキュリティに初めて触れる開発者やインフラ担当者の皆さんに向けて、この巧妙な「DNSを使った隠れ通信(C2通信)」をどうやって見破るのか、一緒に一歩ずつ学んでいきましょう!

—

1. そもそもDNSって何?防犯に例えてみよう

インターネットの世界で、私たちは「google.com」や「github.com」といった覚えやすい名前(ドメイン名)を使って通信していますよね。でも、コンピュータ同士が本当に通信するときは、実は「192.0.2.1」のような数字のIPアドレスで会話をしています。

ここで登場するのがDNS(Domain Name System)です。DNSは、いわば「インターネットの電話帳案内係」です。

家の鍵と電話帳の例え

  • ウェブサイトを見る= 「〇〇さんの家(IPアドレス)に行きたい」と電話帳係に聞いて、住所を教えてもらうこと。
  • 通常のDNS通信= 「この名前の住所を教えて!」と、ごく自然なやり取りをすること。

ところが、悪い奴ら(攻撃者)はこの電話帳係を「別の目的」で悪用します。それが、乗っ取ったパソコンから外へ秘密のデータをこっそり持ち出す「DNSトンネリング」や、自動で意味不明な名前を次々と作り出す「DGA(ドメイン生成アルゴリズム)」です。

泥棒が「この家には盗むものがあるぞ」と、外の仲間へ向けて、郵便受けの隙間から暗号の手紙をバラバラにして外に投げ出しているようなイメージですね。普段みんなが使っている「普通の電話の仕組み」を使っているからこそ、見つけにくいのが厄介なところです。

—

2. 攻撃者はどうやってDNSを悪用するの?

攻撃者が仕掛けてくる手口には、大きく分けて次の2つのパターンがあります。現場の調査でも非常によく遭遇する手口です。

パターンA:DGA(ランダムドメインの乱れ撃ち)

ウイルスに感染したパソコンは、あらかじめプログラムされた計算式(アルゴリズム)に従って、毎日何千もの「意味不明な文字列のドメイン名」を自動的に生成します。
例えば、 xkcdqwp2981z.com や as7df89q2k.net のような文字列です。

攻撃者はその中の一握りのドメインだけを裏でこっそり用意しておき、ウイルスに「今日の合言葉のドメインはこれだ!」と繋がらせて命令を受け取ります。泥棒が毎日ランダムに連絡先を変えるようなもので、警察(セキュリティソフト)が特定の連絡先をブロックしても、すぐに別の連絡先を作られてしまいます。

パターンB:DNSトンネリング(こっそりデータを外に出す)

「会社の機密データを盗み出したいけれど、普通のファイル転送はセキュリティソフトに止められてしまう……」そんなとき、攻撃者はDNSの「名前を教えて」というリクエストの文字の中に、データをこっそり混ぜ込みます。

例えば、 [盗みたいデータの断片].hacker-site.com というドメインにアクセスするようパソコンに命令します。すると、会社のDNSサーバーを経由して、外の攻撃者のサーバーまでデータが届いてしまうのです。「これなら普段の業務の通信に見えるだろう」という、悪知恵の働いた手口ですね。

—

3. どうやって見破るの?SOCの現場での統計的アプローチ

「じゃあ、そんな巧妙な隠れんぼを見破るなんて無理じゃないか……」と思われるかもしれませんが、大丈夫です。人間の普段の行動と、機械(ウイルス)の不自然な行動には、「統計的なクセ(特徴)」の違いが必ず現れます。

SOCの現場では、次のようなポイントに注目してログを分析しています。

1. 文字のランダムさ(エントロピー分析):
人間が打つドメイン名は意味がありますが、DGAが作るものはめちゃくちゃです。文字の「バラエティの豊かさ(エントロピー)」が高いものは怪しいとみなします。
2. 長すぎるドメイン名:
通常のドメインはそこまで長くありませんが、DNSトンネリングではデータを詰め込むため、ドメイン名が異常に長くなります。
3. 存在しないドメインへの問い合わせ(NXDOMAIN):
DGAは適当な名前を大量に作るので、外のDNSサーバーから「そんな名前はありません(NXDOMAIN)」というエラーが大量に返ってきます。このエラーの回数が異常に多い端末は、感染の疑いが濃厚です。

—

4. 実践!ログを分析するためのPythonスクリプト例

「理屈は分かったけれど、実際にどうやって見つけるの?」という方のために、DNSクエリログの中から「怪しい長文ドメインやランダムなドメイン」を簡易的に検出し、集計するためのPythonスクリプトのサンプルを用意しました。

実務のインフラ構築やログ分析のテスト環境で、そのまま参考にして動かしてみてください。

import math
from collections import Counter

# 【解説】ドメイン名の文字列の「ランダムさ(エントロピー)」を計算する関数
# DGAによって生成されたドメインは、文字の偏りが少なく、エントロピーが高くなる傾向があります。
def calculate_entropy(domain):
    if not domain:
        return 0
    
    # 文字の出現頻度をカウント
    counts = Counter(domain)
    entropy = 0
    length = len(domain)
    
    for count in counts.values():
        # 確率を計算
        probability = count / length
        # シャノンのエントロピーの公式を適用
        entropy -= probability * math.log2(probability)
        
    return entropy

# 【解説】サンプルのDNSクエリログ(本来はSIEMやDNSサーバーのログから取得します)
dns_query_logs = [
    {"timestamp": "202X-10-01 10:00:01", "client_ip": "192.168.1.50", "query": "www.google.com"},
    {"timestamp": "202X-10-01 10:00:05", "client_ip": "192.168.1.50", "query": "github.com"},
    # 以下の2つは、怪しい挙動(長すぎる、またはランダムすぎる)を模擬したクエリ
    {"timestamp": "202X-10-01 10:05:12", "client_ip": "192.168.1.105", "query": "a8f7q9w28475nf839201kas8d7f.evil-c2.com"},
    {"timestamp": "202X-10-01 10:05:15", "client_ip": "192.168.1.105", "query": "xkcdqwp2981z99a8sd7f.net"}
]

print("=== DNSクエリログの不審者チェック(統計的分析) ===\n")

# 閾値(この値を超えたら怪しいと判定する目安)
ENTROPY_THRESHOLD = 3.8
LENGTH_THRESHOLD = 25

for log in dns_query_logs:
    domain = log["query"]
    domain_name_part = domain.split('.')[0] # ドメインの主体部分を取得
    
    # エントロピーと文字数の計算
    entropy = calculate_entropy(domain_name_part)
    length = len(domain_name_part)
    
    # 判定フラグ
    is_suspicious = False
    reasons = []
    
    if entropy > ENTROPY_THRESHOLD:
        is_suspicious = True
        reasons.append(f"ランダム性が高い (エントロピー: {entropy:.2f})")
        
    if length > LENGTH_THRESHOLD:
        is_suspicious = True
        reasons.append(f"名前が異常に長い ({length}文字)")
        
    # 結果の出力
    if is_suspicious:
        print(f"[!] 警告: 不審なDNSクエリを検出しました!")
        print(    f"    時刻: {log['timestamp']}")
        print(    f"    端末IP: {log['client_ip']}")
        print(    f"    クエリ: {domain}")
        print(    f"    理由: {', '.join(reasons)}")
        print("-" * 50)
    else:
        print(f"[OK] 正常なクエリ: {domain} (端末: {log['client_ip']})")

print("\n分析が完了しました。日々のログ監視にこのロジックを組み込んでみましょう!")

—

5. まとめと最初の一歩

いかがでしたでしょうか?一見すると難しそうに見える「DNSによるC2通信の検知」も、「家の郵便受けからの不審な手紙(ランダムな文字列や長すぎる名前)を見つけ出す」という視点を持てば、ぐっと身近に感じられたのではないでしょうか。

IT担当者や開発者の皆さんが今日からできる最初の一歩としては、以下のような対策が挙げられます。

  • 社内のDNSサーバー(またはフルリゾルバ)のログを捨てるな!:普段からアクセスログを保存し、エラー(NXDOMAIN)が多発している端末がないか定点観測する。
  • 外部への直接のDNS通信を制限する:社内のパソコンが、勝手に外部のパブリックDNS(例: 8.8.8.8 など)へ直接通信できないよう、ファイアウォールで社内DNSサーバー経由に強制する。

セキュリティ対策に「完璧」はありませんが、こうした地道なログの統計的分析が、大惨事を防ぐ強力な盾になります。焦らず、一歩ずつ日々の運用を強固にしていきましょう!

コメント

タイトルとURLをコピーしました