復号なき狩り:JA3/JA3sフィンガープリンティングで暴くC2通信の「指紋」
ネットワークの最前線に立つ我々にとって、昨今のHTTPSによる暗号化の標準化は、セキュリティ監視における「パンドラの箱」だ。TLS 1.3の普及により、ALPNやSNIすらも暗号化の波に飲まれ、従来のIPSによるシグネチャベースの検知は、もはや過去の遺物となりつつある。
しかし、絶望するにはまだ早い。通信の中身を見ることができないのなら、通信の「作法」そのものを解析すればいい。今日は、TLSハンドシェークの深淵に潜む、C2(Command & Control)通信を炙り出すための強力な武器、JA3 と JA3s について掘り下げていこう。
TLSハンドシェークは「嘘をつかない」
TLSハンドシェークの開始時、クライアントは Client Hello パケットを送信する。ここには、サポートするTLSバージョン、暗号スイート(Cipher Suites)、拡張(Extensions)、楕円曲線、曲線形式などが並ぶ。
マルウェアの作者がどれほど巧妙に偽装を試みても、彼らが利用するライブラリ(WinHTTP や OpenSSL、あるいは独自のスタティックリンクされた実装)の「癖」までは完全に隠しきれない。
JA3の正体:クライアントの「指紋」
JA3 とは、Client Hello の以下のフィールドを連結し、MD5ハッシュ化したものだ。
- TLSバージョン
- 受け入れ可能な暗号スイート
- 拡張リスト
- 楕円曲線
- 楕円曲線形式
このハッシュ値は、特定のライブラリや特定のマルウェアファミリーに対して驚くほど一貫性を示す。ブラウザのフィンガープリントがウェブ閲覧体験を最適化するように、JA3 はマルウェアの「実装の個体差」を浮き彫りにする。
実装:パケットキャプチャからの抽出
現場でこのフィンガープリントを抽出するには、Zeek (旧Bro) を使うのが最もエレガントだが、トランスポート層の挙動を直接理解するために、Pythonの scapy を用いたプロトタイプを考えてみよう。
from scapy.all import *
from scapy.layers.tls.all import *
def extract_ja3(packet):
# TLS Client Helloパケットを捕捉
if packet.haslayer(TLSClientHello):
hello = packet[TLSClientHello]
# フィールドを連結してJA3文字列を作成
# バージョン、暗号スイート、拡張、曲線、曲線形式を整理
version = str(hello.version)
ciphers = "-".join([str(c) for c in hello.cipher_suites])
extensions = "-".join([str(ext.type) for ext in hello.ext])
curves = "" # 必要に応じて楕円曲線情報を抽出
ja3_string = f"{version},{ciphers},{extensions},{curves}"
return hashlib.md5(ja3_string.encode()).hexdigest()
# ライブキャプチャの実行例
sniff(filter="tcp port 443", prn=extract_ja3)
JA3s:サーバー側の「応答」を射抜く
JA3 だけでは不十分な場合がある。クライアントは巧妙に「Chromeのふり」をすることが可能だが、それに対する Server Hello の応答(JA3s)まで完璧に模倣するマルウェアは稀だ。
サーバー側がどのような暗号スイートを選択し、どの拡張を返すか。この JA3 と JA3s のペア(JA3+JA3s)を組み合わせることで、検知精度は飛躍的に向上する。C2サーバー特有の「お決まりの応答」をフィンガープリント化することで、誤検知を極限まで減らしつつ、未知の亜種を追い詰めることができる。
極限のインフラパフォーマンスとセキュリティの調和
大規模なトラフィックを捌くアーキテクトにとって、パケット解析はパフォーマンスとの戦いだ。TLSハンドシェークのRTT(Round Trip Time)を短縮するために TCP Fast Open を有効にしている環境では、ハンドシェークのシーケンスが変化する。
インフラレベルで JA3 を活用する場合、以下のチューニングは必須だ。
1. バッファチューニング: sysctl による net.core.rmem_max の増大。パケットを取りこぼすとフィンガープリントの生成が失敗する。
# 受信バッファを拡大し、高負荷時のドロップを防ぐ
sysctl -w net.core.rmem_max=26214400
2. ヘッダー圧縮の考慮: HTTP/2以降、HPACK 圧縮が標準だが、ハンドシェークの段階ではまだ暗号化のオーバーヘッドが支配的だ。CPUバウンドな処理にならないよう、オフロードNICの活用を検討すべきだ。
結びに代えて:防御は「静的な境界」から「動的な挙動」へ
ゼロトラストアーキテクチャの本質は、境界の消失を前提とした「アイデンティティと挙動の信頼」だ。IPアドレスやポート番号といった、もはや意味をなさないメタデータに頼る時代は終わった。
JA3 は、通信の背後にいる「何者か」を、そのプロトコルスタックの「癖」から特定する。これは単なるパケット解析ではない。ネットワークを走るすべてのパケットに魂を吹き込み、その振る舞いから悪意を読み解く、現代のネットワークエンジニアに必須の「デジタル聴診器」なのだ。
君たちが管理するネットワークの中で、今日も得体の知れない Client Hello が飛び交っている。そのMD5ハッシュの先にある真実を、ぜひ自身の目で確かめてみてほしい。
コメント