エンジニア初心者向けC2 DNSサーバーの仕組みと特徴—セキュリティ対策現場の基本理解

Published on: | Last updated:

「DNSって、どこでも通るから安全」って、うっかり思い込んでない?🙂

答えを先に言うね:DNSベースのC2は「UDP/53(例:533)」と「Base64」と「Aレコード」を使い、問い合わせ名(qname)に命令を埋めて往復する仕組みで、dnslibのDNSRecordで実験できる。(出典:dnslib、RFC 1035

  • まず見る場所:qname(問い合わせ名)に何が入ってるか
  • 通信の形:UDPで短いパケット、DNSは512バイトが基本(EDNS0は別)
  • 隠し方:Base64で文字を「DNSに入りやすい形」に寄せる
  • 返し方:DNS応答に“それっぽい”Aレコードを入れる
  • 注意:これは防御側の理解にも使えるけど、扱い方はほんと慎重に
図1:DNSで命令を往復させる全体像
図1:DNSで命令を往復させる全体像

DNSでやり取りするって結局なに

DNSベースのC2は、DNSクエリとDNSレスポンスを「命令」と「返事」の運搬役にして、感染端末とサーバーが合図し合う通信方式である。(出典:RFC 1035)

DNS:Domain Name System(人が読む名前をIPに引く仕組み)。ほぼ全員が毎日勝手に使ってる。スマホも。テレビも。ルーターも。

でね、そこがミソなんだけど、会社のネットワークってHTTPは見張ってても、DNSは「まあ必要だよね」って通しがち。全部じゃないよ?でも通り道としては太い。

だから悪い人は、そこにメモを貼る。メモ用紙がDNS。そんな感じ。

短いメモしか貼れない。貼れる場所も決まってる。そこが逆に“作りやすい”のが、ちょっと怖いとこ。

中央集権ってやつがラクで危ない

中央集権型C2は、複数の端末が「同じ1台のC2サーバー」にだけ話しかける構造で、命令配布と回収が単純になる。(用語:中央集権C2=単一司令塔モデル)

いい点として語られがち:管理がラク。命令を投げる先が一個。ログも一箇所。

でもね、母目線で言うと…「ラク」ってだいたい事故るの。ほんとに。😮‍💨

中央に寄せると、守る側からすると監視もしやすい“はず”なんだけど、DNSって量が多いから、埋もれる。そこがイヤ。

あと原文にも出てたけど、Dynamic DNS(IP変わっても名前を追随させる仕組み)とか、Fast-Flux(短い間隔でIPを入れ替えて追跡しづらくする運用)みたいなのと相性がいい。やめてほしい。ほんと。

「DNSは“目立たない”んじゃなくて、“多すぎて見落とされる”だけ。」

この仕組みのキモはqnameとBase64

DNS C2の基本は、DNSクエリのqnameに命令を入れて、サーバー側でBase64デコードして解釈し、レスポンスをBase64で返すことである。(用語:qname=問い合わせ名、Base64=バイナリを文字列化する符号化)

qname:たとえば aGVsbG8.example.com みたいなやつ。先頭のサブドメインに“文字列”を押し込む。

原文の例だと、最初のドットまでを命令として抜いてたね。qname.split('.')[0] のところ。

で、その命令がBase64ならデコードする。違ったら平文扱い。ここ、雑に見えるけど、実験としては分かりやすい。

ただしね。

Base64って「暗号」じゃないからね。読める人には読める。これ、絶対に勘違いしないで。お願い。😅

図2:qnameのどこに命令を入れて、どこで取り出すか
図2:qnameのどこに命令を入れて、どこで取り出すか

Python側の実装はここだけ押さえればいい

PythonのDNSサーバーはUDPソケットで待ち受け、dnslibのDNSRecord.parseでDNSを解釈し、DNSRecord.replyで応答を組み立ててsock.sendtoで返す。(出典:dnslib)

原文コードの骨:だいたいこの流れ。

  • UDPで 0.0.0.0:53(環境で埋まってたら533みたいに変える)
  • recvfrom(512) で受ける(古典的DNSサイズ)
  • DNSRecord.parseでパース
  • qnameから命令取り出し
  • 辞書commandsで返事を選ぶ
  • Base64で返事をエンコード
  • replyにAレコードを足して返す

ここで一回、家庭内の小言ね。

53番ポート:普通に使われてるから、ローカル実験だと「もう使ってるよ!」って怒られるの、あるある。533にずらすのは現実的。うん。

それと、原文のコードは“分かること”優先で、細部は荒い。例えば、Aレコードに変な形で文字を詰めるのはDNS的に苦しいところがある。

でも教材として「DNS応答に何か載せる」感覚を掴むには、まあ…最初はこうなるよね、って感じ。

防御の観点での見どころ:DNSのAレコードが、普段の業務の名前解決と比べて不自然な返し方してないか。そこ。

短い。けど効く。

クライアント側は生DNSパケットを投げてる

C++クライアントはWinsockでUDPソケットを作り、DNSHeaderとQNAMEエンコードを自前で組んで、sendtoでDNSサーバーへ投げる。(用語:Winsock=WindowsのソケットAPI、DNSHeader=DNSパケット先頭)

ここ、原文の“学びどころ”が濃い:DNSの名前って、ただの文字列じゃなくて、長さ+ラベルの繰り返しで送るの。

例:ping.subdomain.domain.com

4 ping 9 subdomain 6 domain 3 com 0

こうやって詰める。最後に0。終わりの合図。

あとDNSHeaderのflagsを 0x0100 にして「標準クエリっぽく」してたね。idも 0x1234

こういうの、実際の監視では手がかりになる時がある。固定値ばっかりだと目立つ。ほんと目立つ。

…って言いながら、今ふと思ったけど、開発の最初って固定値にしがちなんだよね。分かる。分かるけど。

守る側の人は「固定の問い合わせID」みたいな癖を拾えると楽。

図3:素直なDNSと、変なDNSの違いが見える比較
図3:素直なDNSと、変なDNSの違いが見える比較

快問快答で3つの迷いを折るよ

規則:ここは迷いがちな3つだけ、短く当てる。現場で転ばないため。

Q:Base64って暗号でしょ?

A:Base64は暗号ではなく符号化で、見れば戻せる。ログに残ったら普通に読まれる。(出典:RFC 4648

Q:DNSはどこでも通るから、検知されないよね?

A:DNSは通りやすいが、今はDNSログ監視やDNS Firewallが普通にあり、qnameの長さ・頻度・文字の偏りで引っかかる。(例:企業のSOC運用、製品例:Microsoft Defender for EndpointのDNS可視化)

Q:ポート53が使えないなら終わり?

A:ポートは533などに変えられるが、社内ネットワークは外向きUDPを絞ることが多く、逆に通らないこともある。まず自分の環境の出口を確認。(出典:一般的なネットワーク運用)

日本でやるならここだけは現実を見る

日本の組織ネットワークでは、社内DNS(例:Active Directory DNS)や外部DNSフィルタリングを前提に監視設計が組まれ、DNSログとEDRが突き合わせられることが多い。

機関っぽい話:日本だと、インシデント対応の話題はIPAの資料がまず出てくるし、JPCERT/CCの注意喚起も参照されやすい。DNSトンネリング系の話も、文脈としてはだいたいそこに合流する。

通路の話:現場の人はTwitter…じゃなくて今はXか、Slackコミュニティとか、あとJPな勉強会のスライド文化で情報が回る。そういう場所で「DNSの変な例」ってだいたい共有されてる。

環境の話:在宅増えてルーター越しの名前解決が混ざると、ログが散らかる。散らかると、見落としやすい。これ、地味に効く。

そして文化の話。日本の組織って「止めたら怒られるから、止めない」って空気が残ることあるでしょ。DNS止めるのは怖い。だから“監視して通す”になりやすい。ここ、攻撃にも防御にも関わる。

「止められない通信ほど、観察のしかたが問われる。」

守る側のためのチェックだけ置いていくね

DNS悪用の兆候は、qnameの高エントロピー文字列、短時間の反復問い合わせ、NXDOMAINの増加、単一ホストからの偏りで見つけやすい。(用語:NXDOMAIN=名前が存在しない応答、エントロピー=文字の散らばり具合)

進んだ指標:ただ「件数」じゃなくて、こういうの見ると刺さる。

  • NXDOMAIN率:存在しない名前ばかり引いてない?(DNSトンネルの試行で増えがち)
  • qnameの文字種:やたらA-Za-z0-9+/=っぽい並び(Base64の匂い)
  • 問い合わせ間隔:秒刻みで同じ形が繰り返される
  • 宛先DNS:普段のリゾルバじゃないIPに直接投げてない?

具体ツール:ここはふわっと言わない。名前を出す。

  • Wireshark:DNSパケットを見て「どこが変か」を目で覚える
  • Sysmon:Event ID 22(DNS query)を使う構成で追える(出典:Microsoft Sysmon
  • Microsoft Defender for Endpoint:端末側のDNS可視化と相関

あ、あとね。DNS over HTTPS(DoH)とかDNS over TLS(DoT)も絡むと、見え方が変わる。

急に難しくなる。ほんと。

でも「どこで復号されるか」を押さえると、話は戻ってくる。戻ってくる…たぶん。😵

図4:最小の防御チェックリスト
図4:最小の防御チェックリスト

結局これ、学ぶ価値あるのか問題

DNSベースC2の学習は、DNSプロトコル、データ埋め込み、検知指標(NXDOMAIN率やqnameエントロピー)を同時に理解でき、防御設計の判断が速くなる。

ここからがちょい本音:攻撃手法を知るの、気持ち悪いよね。分かる。

でも、知らないままだと「DNSは普通の通信だから」で終わっちゃう。終わると、現場で困るのはあなた。

夜中にアラート鳴って、ログ見ても、何が変なのか分からない。あれ、しんどい。

だから、私は「仕組みだけは触って理解して、使い方は線引きする」派。

反対意見も分かるよ。手を出したくない人もいる。うん。

で、ちょっと聞きたい:あなたは「攻撃の中身まで触るのは危ない」派?それとも「触らないと守れない」派?

私は後者寄りだけど、境界線の引き方は人によって違うから、ぶつけてみたい。静かに。ね。

Related to this topic:

Comments