「DNSって、どこでも通るから安全」って、うっかり思い込んでない?🙂
答えを先に言うね:DNSベースのC2は「UDP/53(例:533)」と「Base64」と「Aレコード」を使い、問い合わせ名(qname)に命令を埋めて往復する仕組みで、dnslibのDNSRecordで実験できる。(出典:dnslib、RFC 1035)
- まず見る場所:qname(問い合わせ名)に何が入ってるか
- 通信の形:UDPで短いパケット、DNSは512バイトが基本(EDNS0は別)
- 隠し方:Base64で文字を「DNSに入りやすい形」に寄せる
- 返し方:DNS応答に“それっぽい”Aレコードを入れる
- 注意:これは防御側の理解にも使えるけど、扱い方はほんと慎重に
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って「暗号」じゃないからね。読める人には読める。これ、絶対に勘違いしないで。お願い。😅
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つの迷いを折るよ
規則:ここは迷いがちな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)も絡むと、見え方が変わる。
急に難しくなる。ほんと。
でも「どこで復号されるか」を押さえると、話は戻ってくる。戻ってくる…たぶん。😵
結局これ、学ぶ価値あるのか問題
DNSベースC2の学習は、DNSプロトコル、データ埋め込み、検知指標(NXDOMAIN率やqnameエントロピー)を同時に理解でき、防御設計の判断が速くなる。
ここからがちょい本音:攻撃手法を知るの、気持ち悪いよね。分かる。
でも、知らないままだと「DNSは普通の通信だから」で終わっちゃう。終わると、現場で困るのはあなた。
夜中にアラート鳴って、ログ見ても、何が変なのか分からない。あれ、しんどい。
だから、私は「仕組みだけは触って理解して、使い方は線引きする」派。
反対意見も分かるよ。手を出したくない人もいる。うん。
で、ちょっと聞きたい:あなたは「攻撃の中身まで触るのは危ない」派?それとも「触らないと守れない」派?
私は後者寄りだけど、境界線の引き方は人によって違うから、ぶつけてみたい。静かに。ね。
