技術者ブログ
クラウド型WAF「Scutum(スキュータム)」の開発者/エンジニアによるブログです。
金床“Kanatoko”をはじめとする株式会社ビットフォレストの技術チームが、“WAFを支える技術”をテーマに幅広く、不定期に更新中!

HadoopとDunkheadでハッカーの電脳模擬戦(Defcon 8 CTF)のポートスキャンを可視化する

はじめに
Dunkheadはタイムスタンプ付きのテキストデータ(アクセスログなど)を手軽に可視化するためのソフトウェアです。Hadoop上で動作するため、データのサイズが大きい、いわゆるビッグデータの場合にも使用することができます。
今回は、ハッカーの祭典として知られるDefconで行われた電脳模擬戦(Defcon 8 CTF)のログをDunkheadで可視化し、ポートスキャンやホストスキャンの様子を観察してみます。
PCAP形式のデータをtsharkでテキストデータに変換する
可視化の対象とするデータは、tcpdumpによって収集された、いわゆるPCAP形式のファイル(パケットダンプ)となります。このデータはバイナリデータであり、通常はWiresharkというGUIのツールで開いたり、tcpdumpの-rオプションなどを使って読んだりします。今回はDunkheadを使って解析するため、タイムスタンプの付いた行指向のテキストデータに変換しておく必要があります。
tcpdumpを使うことも可能なのですが、今回はよりわかりやすい出力を行ってくれる、tsharkを使うことにします。
tsharkはWiresharkをインストールすると一緒にインストールされるコマンドラインのアプリケーションで、下記のようにオプションを指定することで、各パケットについて、tcpdumpよりも情報に富んだ形でテキスト情報を吐いてくれます。この例では-tオプションでタイムスタンプを付加しています。
tshark -t ad -r [ data file name ]
出力されるデータは以下のようになります。
1 2000-07-29 22:30:00.774366 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 699 [SYN] Seq=0 Win=4096 Len=0 2 2000-07-29 22:30:00.774397 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 22273 [SYN] Seq=0 Win=4096 Len=0 3 2000-07-29 22:30:00.774449 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 956 [SYN] Seq=0 Win=4096 Len=0 4 2000-07-29 22:30:00.774507 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 322 [SYN] Seq=0 Win=4096 Len=0 5 2000-07-29 22:30:00.774560 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 495 [SYN] Seq=0 Win=4096 Len=0 6 2000-07-29 22:30:00.774610 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 301 [SYN] Seq=0 Win=4096 Len=0
このように、各行にタイムスタンプが付加されたテキストデータになれば、Dunkheadで扱うことが可能になります。
巨大なPCAPファイルにどう向き合うか
Defconをはじめ、CTFやその他のPCAP形式のデータを丸ごとダウンロードできるようになっているサイトはいくつかありますが、場合によってはデータのサイズが全部で数GBや数十GB(数千万から数億パケット)という規模になります。このような場合、もちろんGUIのツールであるWiresharkでは重くて開くことはできず、また、tcpdumpで読むとしても、あらかじめ「何を読むのか?」を決める必要があります。
「何を読むのか?」を決めるためにまず読みたい、という場合に、Dunkheadが有効です。あまり深く考えずにデータを可視化してみることで、全体の特徴や、自分が興味がある要素が自然と浮かび上がってくるようになります。DunkheadはHadoopアプリケーションであるため、Amazon EMRや自前のHadoopクラスタを用いることにより、現実的な時間内で数千万パケットや数億パケットのデータからおおまかな傾向を掴むことができます。
しかし実は一点問題があり、それは上記のようにPCAP形式のデータをtsharkでテキストデータに変換する、という処理が必要だということです。この処理はそこそこ重いので、それこそHadoopクラスタで分散して行いたいような性質のものです。一度変換してしまえばその後はHadoopとDunkheadで繰り返し利用することができるので、最初の一度だけ我慢して時間をかける必要がありそうです。尚、tcpdumpの-rオプションによるテキストデータへの変換は、tsharkのそれよりもかなり高速なので、データがあまりにも大きい場合にはtcpdumpを利用するという選択肢も考えられます。
データのフォーマットを確認する
今回解析対象とするのは比較的小さめの29133000というファイル名のパケットダンプです。サイズはpcap形式で253MB、tsharkによる変換後で227MB、パケットの数は188万です。小さめとはいっても、Wiresharkで開くのはかなり厳しいサイズであり、またtcpdumpですべてのデータを目で読むのは現実的ではない量です。
Dunkheadでデータを可視化する場合、まずは生のデータがどのようなものなのか、先頭の数十行程度でもよいので、実際にエディタで確認します。タイムスタンプ部は、先ほど掲載したデータのうち、以下の太字の部分になります。
1 2000-07-29 22:30:00.774366 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 699 [SYN] Seq=0 Win=4096 Len=0 2 2000-07-29 22:30:00.774397 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 22273 [SYN] Seq=0 Win=4096 Len=0 3 2000-07-29 22:30:00.774449 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 956 [SYN] Seq=0 Win=4096 Len=0 4 2000-07-29 22:30:00.774507 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 322 [SYN] Seq=0 Win=4096 Len=0 5 2000-07-29 22:30:00.774560 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 495 [SYN] Seq=0 Win=4096 Len=0 6 2000-07-29 22:30:00.774610 10.20.11.195 -> 10.20.1.8 TCP 60 38378 > 301 [SYN] Seq=0 Win=4096 Len=0
そこで、設定ファイルを以下のように記述します。
{ "combiner.inmemory": true, "threshold": 100, "rrd": { "step": 5, "heartbeat": 10 }, "datetime": { "format": "yyyy-MM-dd HH:mm:ss", "regex": "^[ ]?[0-9]+ (.{19})" }, "fields": [ { "name": "SYN", "regex": "\\[SYN\\]", "type": "count" }, { "name": "RST", "regex": "\\[RST", "type": "count" } ] }
CTFのパケットダンプであるため、単位時間当たりのデータ量(レコード数)は、一般的なウェブサーバのアクセスログなどに比べてかなり多くなります。そこで、rrd項目のstepとheartbeatをかなり少なめの値(5秒と10秒)に設定してあります。こうすることで、数秒ごとのデータの変化を追いやすくなります。
また、まずはSYNパケットとRSTパケットに注目し、それぞれが経過時間に対してどのように分布しているかを簡単に調べるため、fields項目に2つの要素を定義しています。
SYNパケットの可視化については、SYN ACKパケットは引っかからないように、[SYN]だけがマッチするような正規表現をregex項目に記述しています。また、RSTパケットについてはACKフラグが立っていてもかまわないので、[RSTにマッチするようにしています。
SYNとRSTに注目してパケット数を可視化する
Dunkheadを実行すると、次のような3つの画像が出力されます。



この3つの画像を見ただけで、勘のいい人であれば「派手なスキャンが実行されている可能性がある」ことがわかるかと思います。22時35分から2,3分の間に、大量のSYNパケットとRSTパケットが飛び交っています。 これはつまりSYNパケットに対してポートが閉じていたという反応が繰り返されているということであり、ポートスキャン(あるいはホストのスキャン)が実行されている可能性が高いと考えることができます。
設定ファイルにおいてstep項目の値を5にしているので、縦軸の数値は5秒間あたりのパケット数を示します。つまり、22時36分頃には、5秒間に4千から6千ものSYNとRSTパケットが観測されていることになります。
このように、可視化することによって、大量のデータの中から注目すべき時間帯を絞り込みやすくなります。時間帯がはっきりしたので、ここで直接、パケットデータをgrep等で観察してみます。22時36分35秒のデータをgrepしてみると、以下のように見事なポートスキャンが見つかります。
740680 2000-07-29 22:36:35.019504 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 2 [SYN] Seq=0 Win=2048 Len=0 740681 2000-07-29 22:36:35.019605 10.20.1.13 -> 10.20.12.213 TCP 60 2 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 740682 2000-07-29 22:36:35.019710 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 869 [SYN] Seq=0 Win=2048 Len=0 740683 2000-07-29 22:36:35.019810 10.20.1.13 -> 10.20.12.213 TCP 60 869 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 740684 2000-07-29 22:36:35.019966 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 609 [SYN] Seq=0 Win=2048 Len=0 740685 2000-07-29 22:36:35.020066 10.20.1.13 -> 10.20.12.213 TCP 60 609 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 740686 2000-07-29 22:36:35.020400 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 1502 [SYN] Seq=0 Win=2048 Len=0 740687 2000-07-29 22:36:35.020500 10.20.1.13 -> 10.20.12.213 TCP 60 1502 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 740688 2000-07-29 22:36:35.020519 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 245 [SYN] Seq=0 Win=2048 Len=0 740689 2000-07-29 22:36:35.020619 10.20.1.13 -> 10.20.12.213 TCP 60 245 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 740690 2000-07-29 22:36:35.020657 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 296 [SYN] Seq=0 Win=2048 Len=0 740691 2000-07-29 22:36:35.020757 10.20.1.13 -> 10.20.12.213 TCP 60 296 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 740692 2000-07-29 22:36:35.020965 10.20.12.213 -> 10.20.1.13 TCP 60 33100 > 2030 [SYN] Seq=0 Win=2048 Len=0 740693 2000-07-29 22:36:35.021063 10.20.1.13 -> 10.20.12.213 TCP 60 2030 > 33100 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0
また、同様に22時56分過ぎにも、SYNとRSTのグラフで同じ波形が確認できます。こちらもポートスキャンである可能性があります。
TCPポートスキャンを可視化する
前項で見たように、まずグラフの波形からスキャンの可能性と発生時刻を推測し、次に直接パケットダンプのテキストデータをgrepする、という方法によって、ポートスキャンを見つけることができることがわかりました。
しかし、SYNとRSTが大量に発生するケースとして、例えば、ウェブサーバがダウンし、そこに通常のユーザのアクセスが集まる場合なども考えられます。そこで、次に設定ファイルを以下のようにしてみます(何をやろうとしているかわかりますか?)。
{ "combiner.inmemory": true, "threshold": 100, "rrd": { "step": 5, "heartbeat": 10 }, "datetime": { "format": "yyyy-MM-dd HH:mm:ss", "regex": "^[ ]?[0-9]+ (.{19})" }, "fields": [ { "name": "$1-SYN", "regex": " ([0-9\\.]+) -> .*> ([0-9]+) \\[SYN\\]", "eval": "$2", "type": "average" } ] }
regex項目において、()で囲まれた2つのグループが存在します。1つめが([0-9\\.]+)です。これは『 -> 』の左側にあることからわかるように、パケットの発信元のIPアドレス(Src IP address )になります。そして2つめのグループは([0-9]+)となっており、これは[SYN]の左側の数値、つまりSYNパケットの宛先ポート番号(Dst Port Number)になります。
name項目の値は『$1-SYN』となっており、これは例えば発信元IPアドレスが1.2.3.4の場合は『1.2.3.4-SYN』となります。また、重要なのはeval項目の値が$2であることです。$2はregex項目内の2つめのグループ、つまりSYNパケットの宛先ポート番号を指します。そしてtype項目の値が『average』となっているため、つまり「5秒あたりのSYNパケットの宛先ポート番号の平均値」がグラフの縦軸になることになります。
Dunkheadを実行すると30個弱の画像ファイルが生成され、その中には以下のようなものが含まれます。



3つの画像とも、SYNパケットの宛先ポート番号の平均値がきれいに斜めの線を描いており、これらがポートスキャンであることがはっきりわかります(1つめの画像は残念ながら時間が印字されておらず、グラフから発生時刻の見当が付かないという問題があります。これについてはJRobinの設定で何か改善できるか検討課題としておきます)。
10.20.11.75(1つめの画像)と10.20.12.91(2つめの画像)はともに1000番程度(おそらく1024辺りでしょうか)までしかスキャンしておらず、10.20.30.134(3つめの画像)は6000番ポートあたりまでスキャンしていることも、これらのグラフから確認することができます。
ホストスキャンを可視化する
前項では単位時間ごとの宛先ポート番号の平均値をグラフにすることで、ポートスキャンを見つけられることを示しました。同じように、IPネットワーク中のホストの存在に対するスキャンについても、宛先IPアドレスの第4オクテットをグラフの縦軸とすることで見つけることができる可能性があります。
設定ファイルを以下のようにしてみます。
{ "combiner.inmemory": true, "threshold": 100, "rrd": { "step": 5, "heartbeat": 10 }, "datetime": { "format": "yyyy-MM-dd HH:mm:ss", "regex": "^[ ]?[0-9]+ (.{19})" }, "fields": [ { "name": "Host-Scan-$1", "regex": " ([0-9\\.]+) -> [0-9]+\\.[0-9]+\\.[0-9]+\\.([0-9]+) ", "eval": "$2", "type": "average" } ] }
これによって約100個ほどの画像が生成されます。100個の画像ファイルとなると内容を確認するのが大変そうに思えますが、ファイル管理ツール(WindowsのExplorer、MacであればFinder)でディレクトリ中の画像ファイルをサムネイル表示するようにすることで、100個程度ならば、ざっと数秒程度で内容を確認することができます。筆者はUbuntuのnautilusを使っています。
グラフが斜めの線を描いている画像を探してみると、以下の画像が見つかります。

22時38分から39分にかけて、きれいにグラフが斜めの線を描いています。この部分をgrepしてみると、以下のようなパケットが見つかります。
998172 2000-07-29 22:38:47.797687 10.20.11.205 -> 10.20.1.11 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0 998230 2000-07-29 22:38:48.067697 10.20.11.205 -> 10.20.1.12 ICMP 60 Echo (ping) request id=0x217b, seq=14080/55, ttl=51 998231 2000-07-29 22:38:48.067900 10.20.11.205 -> 10.20.1.12 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0 999089 2000-07-29 22:38:48.347629 10.20.11.205 -> 10.20.1.13 ICMP 60 Echo (ping) request id=0x217b, seq=15360/60, ttl=51 999090 2000-07-29 22:38:48.347831 10.20.11.205 -> 10.20.1.13 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0 999190 2000-07-29 22:38:48.619558 10.20.11.205 -> 10.20.1.14 ICMP 60 Echo (ping) request id=0x217b, seq=16640/65, ttl=51 999191 2000-07-29 22:38:48.619760 10.20.11.205 -> 10.20.1.14 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0 999299 2000-07-29 22:38:48.870051 10.20.11.205 -> 10.20.1.15 ICMP 60 Echo (ping) request id=0x217b, seq=17920/70, ttl=51 999301 2000-07-29 22:38:48.870257 10.20.11.205 -> 10.20.1.15 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0 999617 2000-07-29 22:38:49.131974 10.20.11.205 -> 10.20.1.16 ICMP 60 Echo (ping) request id=0x217b, seq=19200/75, ttl=51 999622 2000-07-29 22:38:49.132665 10.20.11.205 -> 10.20.1.16 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0 999724 2000-07-29 22:38:49.394628 10.20.11.205 -> 10.20.1.17 ICMP 60 Echo (ping) request id=0x217b, seq=20480/80, ttl=51 999725 2000-07-29 22:38:49.394834 10.20.11.205 -> 10.20.1.17 TCP 60 55428 > 80 [ACK] Seq=1 Ack=1 Win=2048 Len=0
10.20.11.205から、10.20.1.11、10.20.1.12、10.20.1.13...という順番に、ICMPとTCP(80番)の両方を使ったホストスキャンが行われていることがわかります。TCPのパケットは、SYNではなくACKを使っているようです。
また、22時40分頃から45分頃にかけても、斜めの線が確認できます。この部分のログを見てみると、次のようになっています。
1245750 2000-07-29 22:42:04.118149 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 2600 [SYN] Seq=0 Win=2048 Len=0 1245752 2000-07-29 22:42:04.118512 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 616 [SYN] Seq=0 Win=2048 Len=0 1245754 2000-07-29 22:42:04.118868 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 883 [SYN] Seq=0 Win=2048 Len=0 1245756 2000-07-29 22:42:04.119224 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 777 [SYN] Seq=0 Win=2048 Len=0 1245758 2000-07-29 22:42:04.119580 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 1551 [SYN] Seq=0 Win=2048 Len=0 1245760 2000-07-29 22:42:04.119937 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 958 [SYN] Seq=0 Win=2048 Len=0 1245762 2000-07-29 22:42:04.120294 10.20.11.205 -> 10.20.1.13 TCP 60 55408 > 487 [SYN] Seq=0 Win=2048 Len=0 (中略) 1247194 2000-07-29 22:42:11.373283 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 1506 [SYN] Seq=0 Win=2048 Len=0 1247196 2000-07-29 22:42:11.373598 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 1504 [SYN] Seq=0 Win=2048 Len=0 1247199 2000-07-29 22:42:11.373918 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 629 [SYN] Seq=0 Win=2048 Len=0 1247201 2000-07-29 22:42:11.374382 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 760 [SYN] Seq=0 Win=2048 Len=0 1247203 2000-07-29 22:42:11.374699 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 586 [SYN] Seq=0 Win=2048 Len=0 1247205 2000-07-29 22:42:11.375050 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 870 [SYN] Seq=0 Win=2048 Len=0 1247207 2000-07-29 22:42:11.375381 10.20.11.205 -> 10.20.1.14 TCP 60 55408 > 397 [SYN] Seq=0 Win=2048 Len=0 (中略) 1251994 2000-07-29 22:42:19.399329 10.20.11.205 -> 10.20.1.15 TCP 60 55408 > 2010 [SYN] Seq=0 Win=2048 Len=0 1251996 2000-07-29 22:42:19.399673 10.20.11.205 -> 10.20.1.15 TCP 60 55408 > 944 [SYN] Seq=0 Win=2048 Len=0 1251998 2000-07-29 22:42:19.400019 10.20.11.205 -> 10.20.1.15 TCP 60 55408 > 447 [SYN] Seq=0 Win=2048 Len=0 1252000 2000-07-29 22:42:19.400370 10.20.11.205 -> 10.20.1.15 TCP 60 55408 > 212 [SYN] Seq=0 Win=2048 Len=0 1252002 2000-07-29 22:42:19.401078 10.20.11.205 -> 10.20.1.15 TCP 60 55408 > 623 [SYN] Seq=0 Win=2048 Len=0 1252004 2000-07-29 22:42:19.401741 10.20.11.205 -> 10.20.1.15 TCP 60 55408 > 464 [SYN] Seq=0 Win=2048 Len=0
SYNパケットによる広範囲のポートスキャンを、順番に各IPアドレスに対して試行していることがわかります。22時38分頃のスキャンとは異なる性質のスキャンになっており、複数パターンのスキャンを試している様子が見て取れます。TCPで広範囲のスキャンを行っているために先ほどのICMPとTCP80番のみのスキャンより時間がかかっているため、グラフの傾きはゆるやかです。
22時48分ごろから、また傾きが急なスキャンが確認できます。こちらはgrepしてみると以下のようなパケットであることがわかります。
1683820 2000-07-29 22:48:00.223908 10.20.11.205 -> 10.20.11.59 ICMP 60 Echo (ping) request id=0x094b, seq=8705/290, ttl=47 1683823 2000-07-29 22:48:00.225003 10.20.11.205 -> 10.20.11.59 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1683843 2000-07-29 22:48:00.482493 10.20.11.205 -> 10.20.11.60 ICMP 60 Echo (ping) request id=0x094b, seq=9985/295, ttl=47 1683844 2000-07-29 22:48:00.483233 10.20.11.205 -> 10.20.11.60 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1683860 2000-07-29 22:48:00.742602 10.20.11.205 -> 10.20.11.61 ICMP 60 Echo (ping) request id=0x094b, seq=11265/300, ttl=47 1683862 2000-07-29 22:48:00.745210 10.20.11.205 -> 10.20.11.61 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1683891 2000-07-29 22:48:01.003668 10.20.11.205 -> 10.20.11.62 ICMP 60 Echo (ping) request id=0x094b, seq=12545/305, ttl=47 1683894 2000-07-29 22:48:01.004805 10.20.11.205 -> 10.20.11.62 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1683963 2000-07-29 22:48:01.263510 10.20.11.205 -> 10.20.11.63 ICMP 60 Echo (ping) request id=0x094b, seq=13825/310, ttl=47 1683966 2000-07-29 22:48:01.264606 10.20.11.205 -> 10.20.11.63 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684020 2000-07-29 22:48:01.523413 10.20.11.205 -> 10.20.11.64 ICMP 60 Echo (ping) request id=0x094b, seq=15105/315, ttl=47 1684023 2000-07-29 22:48:01.524505 10.20.11.205 -> 10.20.11.64 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684043 2000-07-29 22:48:01.782153 10.20.11.205 -> 10.20.11.65 ICMP 60 Echo (ping) request id=0x094b, seq=16385/320, ttl=47 1684045 2000-07-29 22:48:01.783072 10.20.11.205 -> 10.20.11.65 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684098 2000-07-29 22:48:02.043221 10.20.11.205 -> 10.20.11.66 ICMP 60 Echo (ping) request id=0x094b, seq=17665/325, ttl=47 1684101 2000-07-29 22:48:02.044332 10.20.11.205 -> 10.20.11.66 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684156 2000-07-29 22:48:02.563013 10.20.11.205 -> 10.20.11.68 ICMP 60 Echo (ping) request id=0x094b, seq=20225/335, ttl=47 1684159 2000-07-29 22:48:02.564110 10.20.11.205 -> 10.20.11.68 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684177 2000-07-29 22:48:02.822921 10.20.11.205 -> 10.20.11.69 ICMP 60 Echo (ping) request id=0x094b, seq=21505/340, ttl=47 1684180 2000-07-29 22:48:02.824020 10.20.11.205 -> 10.20.11.69 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684249 2000-07-29 22:48:03.082840 10.20.11.205 -> 10.20.11.70 ICMP 60 Echo (ping) request id=0x094b, seq=22785/345, ttl=47 1684252 2000-07-29 22:48:03.083940 10.20.11.205 -> 10.20.11.70 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684275 2000-07-29 22:48:03.341545 10.20.11.205 -> 10.20.11.71 ICMP 60 Echo (ping) request id=0x094b, seq=24065/350, ttl=47 1684277 2000-07-29 22:48:03.342467 10.20.11.205 -> 10.20.11.71 TCP 60 62094 > 80 [ACK] Seq=1 Ack=1 Win=4096 Len=0 1684295 2000-07-29 22:48:03.601310 10.20.11.205 -> 10.20.11.72 ICMP 60 Echo (ping) request id=0x094b, seq=25345/355, ttl=47
再び、ICMPとTCP80番ACKによるスキャンが実行されていることがわかります。
まとめ
今回は、DunkheadによってPCAP形式のパケットダンプデータを可視化し、ポートスキャンやホストスキャンを見つけてみました。ポートスキャンやホストスキャンはグラフにきれいに特徴が出るため、可視化が非常に有効なことがわかりました。
しかし、この方式にも欠点はあります。例えば、グラフを描画するstepに対して非常に速度が速いスキャンが行われた場合、斜めの線がつぶれてしまい、斜めに見えなくなってしまう可能性があります。 ポートスキャンやホストスキャンを漏れなくより正確に見つけたい場合には、パケット数と宛先ポート番号の種類の比率を計算するなど、より目的に沿ったコードを書く必要があるでしょう。このときMap/Reduceフレームワーク上のコードとして実装すれば、パケットダンプが大きな場合でも対応できるでしょう。
Dunkheadは、タイムスタンプ付きのデータであればパケットダンプに限らず、様々な種類のデータを可視化できる汎用性の高さを持っています。今回試してみたように、まず手軽にデータを覗いてみる用途で使うことを想定して開発されており、より詳細な分析は、データに合わせた専用のコードを書くことを前提にしています。
本エントリやDunkheadに関するフィードバックは、お気軽に@kinyukaまでお寄せください。