DARK MATTER

CDI Engineer's Technical Blog

(続)WiresharkのDissectorを使った独自プロトコル解析をやさしく解説してみました

本稿では、初めて実際に独自プロトコルのDissectorを作る人が最初にぶつかるであろう壁を乗り越える方法を紹介します。

Dissectorって何?という人は、先にWiresharkのDissectorを使った独自プロトコル解析をやさしく解説してみましたを読んでください。

前回は、符号なし整数、文字列を題材にDissectorを説明しました。しかし、実際にDissectorを作ろうとすると、Bit列の解析や時刻の解析など様々なデータ型への対応や、エンディアン(Endian)の解析の壁にぶつかったりします。
今回紹介する内容を理解いただくと、これらの壁を乗り超えてDissectorを作る力がつきます。
サンプルのキャプチャデータも添付するのでWiresharkを使って試してみてください。

サンプルを探す前に、あと一歩力をつけて

技術部の安井です。長年、制御システムを開発した経験から、現在は制御システムセキュリティを見ています。

前回、予想以上に多くの人に読んでいただきありがとうございました。Dissector を作ろうとされている方が大勢いることが分かり嬉しい限りです。

多くの方は、初めて独自プロトコルのDissectorを作ろうとしたときに、サンプルコードを検索して読解できず混乱する人が多いのではないでしょうか?私がそうでした。そうならないために、あと一歩、今回記載する3つのポイントを理解して力をつけてから、検索に出発することをお勧めします。

  • 1. 記法の違いによる混乱に陥らない方法
  • 2. 様々なデータ型の記法の見つけ方
  • 3. エンディアンの理解と対応方法

これを理解すると、サンプルコードを探し回らなくてもDissectorが作成できますし、サンプルコードを見たときに、だいぶストレスなく読めるようになると思います。

できるようになること

様々なデータ型と、エンディアンの違いを含む独自プロトコルを解析するためのDissector作成

記載していないこと

フォーマットが条件により変化するプロトコルのDissectorの作り方。
・例1:パケット内部の値の条件によりフォーマットが変わる内容を表示する。
・例2:データサイズが大きく可変であり、パケット内部のデータサイズ値の条件により複数パケットに分割(フラグメント)された内容をもとに戻して(リアセンブル)表示する。

Dissectorを作る際の3つのポイント

この章は、本ページ内に記載したサンプルコードと見比べて読んでください。

1. 記法の違いによる混乱に陥らない方法

参考サイト毎の異なるコーディングについて

初めて実際にDissectorを作成しようとする人がネット上でサンプルコードを探すと、同じ事をしているようなのに記法が異なるコードが見つかり、どれを参考としてよいか混乱することがあります。

代表的なものが、「ProtoField」と「treeitem:add」の書き方です。「ProtoField」に関しては、例えばProtoField.new("サイズ","origin.uint",ftypes.UINT32)と書いている場合と、ProtoField.uint32("origin.uint","サイズ") と書いている場合があります。両者は同じことを意味しており、前者がデータ型を引数(ftypes.UINT32)で指定しているのに対し、後者はメソッド(uint32)で指定しています。

結局わかりやすいのは公式サイトのマニュアル

コードの記法の違いで混乱しないためには、コードの仕様を確認する方法を知っておく必要があります。コードの仕様を確認するにはWiresharkの公式サイトの「Wireshark Developer’s Guide」が便利です。「ProtoField」の章を見ると、引数で指定することもメソッドで指定することもできることが分かります。筆者はデータ型を引数渡しする方が好みですのでProtoField.newの記法を使っています。treeItem:addについても公式サイトに記載されています。

2. 様々なデータ型の記法の見つけ方

実際にDissectorを書こうと思うと、bit列や、時刻など、様々なデータ型に対応したい場面が出てきます。例えば、ネットワーク上で時刻の情報がUNIX型のバイト列「5e 71 7b 3f」で流れているものを人間が分かる形式の「Mar 18, 2020 10:37:03」と表示したい場合などです。 ネット上でコードを探すのもよいですが、ここでもWireshark の公式サイトの「Wireshark Developer’s Guide」を読むと記法を見つけられるでしょう。「ProtoField」の章を見ると、以下のとおり利用できるデータ型が記載されています。上記の例の時刻はftypes.ABSOLUTE_TIMEが使えます。

Field Type: one of: ftypes.BOOLEAN, ftypes.CHAR, ftypes.UINT8, ftypes.UINT16, ftypes.UINT24, ftypes.UINT32, ftypes.UINT64, ftypes.INT8, ftypes.INT16, ftypes.INT24, ftypes.INT32, ftypes.INT64, ftypes.FLOAT, ftypes.DOUBLE , ftypes.ABSOLUTE_TIME, ftypes.RELATIVE_TIME, ftypes.STRING, ftypes.STRINGZ, ftypes.UINT_STRING, ftypes.ETHER, ftypes.BYTES, ftypes.UINT_BYTES, ftypes.IPv4, ftypes.IPv6, ftypes.IPXNET, ftypes.FRAMENUM, ftypes.PCRE, ftypes.GUID, ftypes.OID, ftypes.PROTOCOL, ftypes.REL_OID, ftypes.SYSTEM_ID, ftypes.EUI64 or ftypes.NONE.

出展:Wireshark Developer's Guide Version 3.3.0

3. エンディアンの理解と対応方法

独自プロトコルの通信データを解析する際は、エンディアンについて理解する必要があります。

エンディアンについて

エンディアンとは、複数のバイトを並べる順序のことで、ビッグエンディアンとリトルエンディアンがあります。プロセッサの違いにより、ビッグエンディアンのプロセッサとリトルエンディアンのプロセッサが存在し、Intelはリトルエンディアンで、SPARCはビッグエンディアンです。
例えば、10進数で51814は16進数で表すと「ca 66」となりますが、この値を4バイトのメモリに格納すると、ビッグエンディアンのプロセッサでは「00 00 ca 66」と格納されるのに対し、リトルエンディアンのプロセッサでは、「66 ca 00 00」と格納され、16進数表記と反対の並びになります。
ネットワーク上にデータを送る場合に、どちらで送るかを意識して通信する必要があります。
なお、ネットワーク通信において、TCP/IPでは、パケットのヘッダ部はビッグエンディアンが用いられていますが、ヘッダ以降の部分(ペイロード)は、通信を行うアプリケーションの仕様で決められます。

Wiresharkでリトルエンディアンのデータを表示する場合は、リトルエンディアン専用のメソッドを利用して変換する必要があります。ビッグエンディアンとリトルエンディアンの表示イメージを図1に示します。

f:id:yasuikj:20200318003938p:plain
図1:ビッグエンディアンとリトルエンディアンの表示

現実世界のエンディアンの実情の例

現在、Intel系のプロセッサが多いためか、ネットワーク上に流れるパケットのペイロード部分が、リトルエンディアンとなっている事も多いです。通信相手がお互いリトルエンディアンであれば、誰もエンディアンを意識しなくてよいので都合が良いというのが理由の1つでしょう。

では、ペイロードをビッグエンディアンとするのはどのような場合があるかというと、Javaアプリケーションはプロセッサによらずビッグエンディアンです。また、制御システムでは現在でもビッグエンディアンを用いている場合が比較的多いようです。

昔は、サーバといえばビッグエンディアンであるSun MicrosystemsのSPARCのサーバが多く、TCP/IPがパケットのヘッダ部がビッグエンディアンであることもあり、ネットワーク上のパケットは全てビッグエンディアンという仕様が多くなったという事もあるのではないかと思います。パケットのバイト列を見て脳内解析する際に、ビッグエンディアンだと16進数と同じ並びなので人間が理解しやすいというのも理由だったのかもしれません。制御システムはライフサイクルが長いですし多くの装置間で通信しているので、一度通信仕様がビッグエンディアンと決まると、装置の一つがリトルエンディアンの新しい装置に変わったとしても、相手がビッグエンディアンのままであれば、通信仕様をリトルエンディアンに変えるわけにはいきません。このような経緯も制御システムでビッグエンディアンを用いているものが比較的多い理由の1つでしょう。

昔よくあったエンディアン問題

SPARC勢が減りだしIntel勢が増えてきたころ、Intlの計算機どおしでエンディアンなど何も考えずにリトルエンディアンで通信しているシステムにて、あるとき、熟練さんがSPARCの装置をネットワークに繋げて通信すると繋がらず、「なんでリトルで流すんだ!昔からネットワークはビッグが普通だろ!」と怒り出すというような場面が見られていたものです。最初に作った方は運用してしまっているので変更できず、後から作った方は、泣く泣く、リトルエンディアンにあわすとかあったものです。
こんなことにならないよう、エンディアンの仕様は明確にしましょう。

リトルエンディアンのデータの表示について

パケット上の各データのエンディアンは、通信仕様によるので、リトルエンディアンのデータは、Dissectorでリトルエンディアンであることを意識して表示する必要があります。ここでは2種類の記法を説明します。
1つ目は、ツリーにaddする際に、 addメソッドの代わりにadd_leメソッドを使う方法です。
2つ目は、ツリーにaddする際に、バッファの値をbuffer(0,4):le_uintなどのメソッドで変換したものを、addメソッドの引数として渡す方法です。具体的なコードは、以下に示すサンプルコードを参照してください。

様々なデータ型(Uint32型、Bit型、bool型、時刻型、byte型)および リトルエンディアンのサンプル

参考として、ここまでの説明した内容のうち、各データ型の使い方、および、add_leメソッドを使ったエンディアン変換のサンプルコードを記します。

-----------------------------------------------------------
-- プロトコルの定義
proto = Proto("originalCS","独自制御プロトコル")

-- プロトコルフィールド定義
-- ProtoField.new(name,abbr,type,[valuestring],[base],[mask],[desc])
-- 引数:
--      name: プロトコルフィールド名、treeに表示される
--      abbr: プロトコルフィールドのフィルタ名
--      type: データ型
--      valuestring(省略可): (本サンプルでは使わないので説明省略) (本サンプルではbit表示時のみnilとして使用)
--      base(省略可): 表示オプション  例: base.DEC 10進表示, base.HEX 16進表示 (本サンプルではbit表示時のみ使用)
--      mask(省略可): データ型のうち、表示対象とするbit位置を指定する (本サンプルではbit表示時のみ使用)
--      desc(省略可): プロトコルフィールドの説明 (本サンプルでは未使用)
--
uint_F  = ProtoField.new("uint符号なし4byte整数","originalCS.uint",ftypes.UINT32)
-- uint_F  = ProtoField.uint32("originalCS.uint","uint符号なし4byte整数") --参考:メソッド記法 上の行と同じ意味
flag1_F  = ProtoField.new("bitフラグ1","originalCS.flag1",ftypes.UINT8,nil,base.DEC,0x80)
-- flag1_F  = ProtoField.uint8("originalCS.flag1","bitフラグ1",base.DEC,nil,0x80) --参考:メソッド記法 上の行と同じ意味
flag2_F  = ProtoField.new("bitフラグ2","originalCS.flag2",ftypes.UINT8,nil,base.DEC,0x40)
-- flag2_F  = ProtoField.uint8("originalCS.flag2","bitフラグ2",base.DEC,nil,0x40) --参考:メソッド記法 上の行と同じ意味
bool_F  = ProtoField.new("bool真/偽","originalCS.bool",ftypes.BOOLEAN)
-- bool_F  = ProtoField.bool("originalCS.bool","bool真/偽") --参考:メソッド記法 上の行と同じ意味
time_F  = ProtoField.new("日時分秒","originalCS.time",ftypes.ABSOLUTE_TIME,nil,base.LOCAL)
-- time_F  = ProtoField.absolute_time("originalCS.time","日時分秒",base.LOCAL) --参考:メソッド記法 上の行と同じ意味
value_F = ProtoField.new("BCD制御値","originalCS.value",ftypes.BYTES)
-- value_F = ProtoField.bytes("originalCS.value","BCD制御値") --参考:メソッド記法 上の行と同じ意味

uint_LittleEndian_F  = ProtoField.new("uint符号なし4byte整数(LittleEndian)","originalCS.uint_LittleEndian",ftypes.UINT32)

-- プロトコルフィールド定義をプロトコルフィールド配列へ登録
proto.fields = {uint_F, flag1_F, flag2_F, bool_F, time_F, value_F, uint_LittleEndian_F}

-- Dissector
function proto.dissector(buffer, pinfo, tree)

    -- パケットインフォメーション情報のprotocolヘッダに表示する名称を設定
    pinfo.cols.protocol = "ORIGINAL_CS"

    -- パケット詳細部のツリーの登録
    local subtree = tree:add(proto,buffer())
    subtree:add(uint_F, buffer(0,4))
    subtree:add(flag1_F, buffer(4,1))
    subtree:add(flag2_F, buffer(4,1))
    subtree:add(bool_F, buffer(5,1))
    subtree:add(time_F, buffer(8,4))
    subtree:add(value_F, buffer(12,2))

    subtree:add("------エンディアン変換の例-----")
    subtree:add_le(uint_F,buffer(14,4))
    --参考:表示内容を、第3引数に直接渡す記法。 表示内容は 14から4byte分をエンディアン変換したもの 上の行と同じ表示となる
    -- subtree:add(uint_F,buffer(14,4),buffer(14,4):le_uint())
end

--  定義したプロトコル(Proto:original)をTCPポート番号を指定して既存のTCPのDissectorに紐づける
tcp_table = DissectorTable.get("tcp.port")
tcp_table:add(3334, proto)

このDissectorを使って「00 00 00 12 80 01 00 00 60 00 40 47 07 53 12 00 00 00」というデータを表示したものを図2に示します。

f:id:yasuikj:20200318164557p:plain
図2:独自プロトコルツリー
図の中のbyte型で表示しているBCDとは、二進化十進数です。
リトルエンディアンの表示は、add_le メソッドで表示しています。

上記の説明で用いた[ORIGINAL_CS]行のキャプチャデータをテキスト形式で以下に記載します。
記載内容をsample.txtというファイル名で空のテキストファイルに転記し保存した上、Wiresharkのメニューの[ファイル]-[開く]からsample.txtを選択してください。
上記の内容と同じ内容がWireshark上で表示されます。テキストファイルの内容を変更してどのような表示になるか確認してみてください。

+---------+---------------+----------+
10:38:17,642,540   ETHER
|0   |08|00|27|c8|48|c2|0a|00|27|00|00|00|08|00|45|02|00|46|00|00|40|00|40|06|49|52|c0|a8|38|01|c0|a8|38|0a|c8|82|0d|06|f0|28|34|e9|84|a0|e3|b5|80|18|08|0a|9c|59|00|00|01|01|08|0a|10|13|88|fe|00|f1|5b|3c|00|00|00|12|80|01|00|00|60|00|40|47|07|53|12|00|00|00|

+---------+---------------+----------+

まとめ

本稿では、実際の独自プロトコルのDissectorを作る際に必要となる3つのポイントを説明しました。

  • 1. 記法の違いによる混乱に陥らない方法
  • 2. 様々なデータ型の記法の見つけ方
  • 3. エンディアンの理解と対応方法

本稿を理解することで、実際に独自プロトコルのDissectorを作れるようになっていただけたら幸いです。

 制御システムセキュリティに関わる者として、このようなノウハウを使って独自プロトコルの平文通信が解析されうることを手を動かして実体験として体感した上で、暗号化の要否などセキュリティ対策を検討いただくことを願います。わからない敵は怖いですが、一つ一つ仕組みを理解し基礎体力をつけていくことで、不安を一つ一つ減らしていくことができるのではないでしょうか?

今回記載しなかったことは、ほぼプログラミングの内容ですので、いつか機会があれば紹介したいと思います。

WiresharkのDissectorを使った独自プロトコル解析をやさしく解説してみました


本稿では、基本的なDissectorの作り方と、Dissectorを活用したパケット解析方法を紹介します。

WiresharkのDissectorをご存知でしょうか?DissectorはWiresharkのプロトコル解析部分で、バイト列を人が理解できる内容に変換し表示してくれます。
Wiresharkを使った事がある方なら、独自プロトコルのバイト列を人が理解できる表示にできないかなぁと思った経験があると思います。
Dissectorを自作しPluginとして追加すると独自プロトコル解析が容易になります。

なぜ今Dissectorを紹介するの?

技術部の安井です。 長年、制御システムを開発した経験から、現在は制御システムセキュリティを見ています。

現在、世の中の多くのプロトコルに対応したDissectorがWiresharkに搭載されています。しかし、制御システムやIoT機器など独自プロトコルのネットワーク通信は数多く存在しており、Dissectorの存在を知らずバイト列と格闘している方が大勢いるようです。

Dissectorの作り方の記述は、ネット上で見つかりますが、コーディングできない人にとっては、少しハードルが高く感じるかもしれません。また、Dissectorをフィルタやグラフと対応させて、解析にうまく活用できていない人もいるかもしれません。そのような方たちに、Dissectorの自作は簡単であり、うまく活用すると解析がとても容易になることを紹介したいと考えた次第です。

対象読者

Wiresharkを利用したことがあり独自プロトコル解析を行いたい人。
"Wireshark" "独自プロトコル" "Dissector" "lua" などを検索して調べたが、プログラムが理解できず諦めてしまった人。
Dissectorを作ってみたが、いざパケット解析しようとしたときに、表示フィルタ、表の列、入出力グラフと連動させて使いこなせていない人。

準備するもの

Wiresharkをインストール済みのPC

できるようになること

独自プロトコル解析するためのDissector作成
Dissectorコーディング内容がWiresharkの表示のどこに関連づくかの対応理解。
DIssectorを活用した解析(表示フィルタ、表への列追加、入出力グラフの表示)

記載していないこと

Wiresharkのインストール方法、Wiresharkの基本的な使い方、Luaが無効となっているWiresharkでLuaを有効とする方法。
効率的なプログラミング( 本稿は、Dissectorの理解に特化するため、変数を極力使わない内容としてあります。 )

Dissectorを入れる前と後での表示の違い

プログラムの説明の前に、自作したDissectorを使うと、どのように解析が容易になるかを説明します。説明に使用する独自プロトコルのデータは、0x00000008 576f6f6f というバイト列とします。

標準Wiresharkでの表示

図1に、自作したDissectorを入れていない標準状態のWiresharkでキャプチャした結果を示します。

f:id:yasuikj:20200313120828p:plain
図1:独自プロトコルのデータと標準Wiresharkでの表示
tree部分に、「00000008576f6f6f」 というbyte列が表示されています。
このようなbyte列を見ると、多くの人が「tree 部分に人が理解できる内容で表示をしてほしい」、「表の列にも表示したい」、「表示した内容でフィルタをかけて表示したい」と思うでしょう。

自作したDissectorを入れたWiresharkでの表示

図2に、自作したDissectorを入れたWiresharkでキャプチャした結果を示します。

f:id:yasuikj:20200313121425p:plain
図2:独自プロトコルデータとDissectorを入れたWiresharkでの表示
tree部分に、「独自プロトコル」、「独自プロトコルのサイズ:8」、「独自プロトコルの文字列:Wooo」 と表示されています。
pinfo部分に、「独自プロトコルのサイズ」「独自プロトコルの文字列」という列が追加され、「8」「Wooo」と表示されています。

treeとpinfoに表示されているので、解析が容易そうですね。

表示フィルタ、グラフ、統計表示

通信データの解析をする際は、表示フィルタ、統計(入出力グラフ)、統計(プロトコル階層)などを使うと便利です。

f:id:yasuikj:20200313121600p:plain
図3:"Wooo"でフィルタ、統計、グラフ表示
図3は、treeに登録した「独自プロトコルの文字列:Wooo」で表示フィルタをかけ、表示フィルタで絞り込んだ状態の「統計」ー「プロトコル階層」を表示した例です。右下部には、「統計」ー「入出力グラフ」を表示し、「独自プロトコルの文字列:Wooo」で表示フィルタをかけたグラフを表示しています。

このように、フィルタ、グラフなどを使うと、更に解析が容易になります。

Dissectorプログラムと登録手順

ここでは、前項までに紹介した表示を可能とするDissectorのプログラムと、DissectorプログラムをWiresharkに登録する方法を説明します。

Dissectorプログラム

Dissectorプログラム(sample_begginer.lua)は以下のとおりです。 -- で始まる行はコメント部分であり、実質11行のプログラムです。

-- sample_begginer.lua の概要説明
-- 解析対象とする独自プロトコルの説明:
-- 独自プロトコルのプロトコルとポート番号
--   TCP 3333
-- 独自プロトコルの名称
--   original
-- 独自プロトコルの構造
--  ------------------------------------------------------------------
--  | oritinal_size(4byte符号なし整数)) | original_data(4byte文字列) |
--  | 独自プロトコルのサイズ      |独自プロトコルの文字列     |
--  ------------------------------------------------------------------
--
-- Dissectorの構造
--  Dissectorテーブル     プロトコル      プロトコルフィールド
--  [DissectorTable:tcp.port]
--                     |---[Proto:original]
--                                  |---[ProtoField:original.original_size]
--                                  |---[ProtoField:original.original_data]
-- DissectroのTree構造
--      [tcp:Transmission Control Protocol]
--      [oritinal:独自プロトコル]
--              |---[original.original_size:独自プロトコルのサイズ]
--              |---[original.original_data:独自プロトコルの文字列]
-- sample_begginer.lua プログラム
-- プロトコルの定義
--    引数: Proto(A,B)
--          A:プロトコルの名称, B:プロトコルの概要
proto = Proto("original","独自プロトコル")

-- プロトコルフィールド定義
--  引数:  ProtoField.new(A, B, C)
--          A:プロトコルフィールドの名称(ツリーに表示される), B:フィルタ名, C:型式
--  概要: 独自フィールドのフォーマットを定義する。プロトコルフィールド定義したものはフィルタ対象にできる。
--  注意: プロトコルフィールド定義していないとフィルタ対象にできないため、フィルタ対象としたいものは、ここに登録する
original_size_F = ProtoField.new("独自プロトコルのサイズ","original.original_size",ftypes.UINT32)  -- 型式はuint32 4byte符号なし整数で定義
original_data_F = ProtoField.new("独自プロトコルの文字列","original.original_data",ftypes.STRING)  -- 型式はstring 文字列で定義
-- プロトコルフィールド定義をプロトコルフィールド配列へ登録
--  概要:  フィルタ可能対象、pinfoの列表示可能対象としたいプロトコルフィールド定義をフィールド配列へ登録する。
--  注意:  プロトコルフィールド配列に登録していないフィールドは、フィルタ対象とできない。フィルタ対象としたいものは、ここに登録する
proto.fields = {original_size_F, original_data_F}
-- Dissector
--  引数:   dissector(buffer,pinfo,tree)
--          buffer: パケット全体イメージのうち、独自プロトコル以降のデータが格納されている。
--          pinfo : パケットインフォメーション情報
--          tree  : パケット詳細部の情報
function proto.dissector(buffer, pinfo, tree)
    -- パケットインフォメーション情報のprotocolヘッダに表示する名称を設定
    pinfo.cols.protocol = "ORIGINAL"

    -- パケット詳細部のツリーの登録
    --   概要:パケット詳細部のツリーに表示する内容を定義する。ツリーは階層化可能。
    --   引数:
    --    *:add ( A, B, C )
    --       A:プロトコルフィールド定義, B:バッファ範囲, C:ツリー表示内容   (A,B,Cとも省略可能)
    --       A,B 両方を指定すると、AのフォーマットでBの内容がツリーに表示される。(本サンプルでは、この方式を使用)
    --       (参考)
    --       Aを指定していると、フイルタ指定、カラム登録が可能となる。
    --       Bを指定していると、ツリーを選択した際に、該当データbyte部分を反転表示するので対象範囲が見やすくなる。
    --       Cを指定していると、Bの内容に関わらずCの内容がツリーに表示される。(本サンプルでは未使用)

    -- ツリーに、サブツリーとしてproto(original)を追加。 bufferをprotoのフォーマット(独自プロトコル)で表示
    local subtree = tree:add(proto, buffer())
    -- proto(original)ツリーに original_size_Fを追加。bufferの0byte目から4byte分をoriginal_size_Fのフォーマットで表示
    subtree:add(original_size_F, buffer(0,4))
    -- proto(original)ツリーに original_data_Fを追加。bufferの4byte目から4byte分をoriginal_data_Fのフォーマットで表示
    subtree:add(original_data_F, buffer(4,4))
end
--  定義したプロトコル(Proto:original)をTCPポート番号を指定して既存のTCPのDissectorに紐づける
tcp_table = DissectorTable.get("tcp.port")   -- 既存のTCP dissector
tcp_table:add(3333, proto)                   -- 既存のTCP dissectorに、ポート番号3333を指定proto(Proto:original)を紐付け

プログラムコードとWireshark画面の表示項目の関係

図4に、プログラムのコードとWiresharkの表示項目の関係を示します。これを見ながらソースコードを読むとソースコードの内容が理解しやすいと思います。

f:id:yasuikj:20200313115244p:plain
図4:プログラムソースとWireshark画面の表示項目の関係

Dissectorプログラムの登録手順

図5を参考に、sample_begginer.lua プログラムを、[Wireshark]-[About Wireshark]-[フォルダ]で表示する「個人Luaプラグイン」もしくは「グローバルLuaプラグイン」のパスへ、sample__begginer.luaという名称で保存してください。

f:id:yasuikj:20200311194953p:plain
図5:Dissectorの登録場所
保存した後、Wiresharkを起動しなおすか、[分析]-[Luaプラグイン再読込]でDissectorが有効となります。

Dissectorを利用した解析

ここでは、Dissectorを利用した解析を行う際に知っておくと便利である表へ列を追加する手順、表示フィルタをかける手順、および、入出力グラフでフィルタをかける手順を説明します。

表へ列を追加する手順

図6に示すように、tree に表示した「独自プロトコルのサイズ」を右クリック - [列として適用] すると、

f:id:yasuikj:20200312181733p:plain
図6:表示項目を列として適用

図7に示すように、表に、「独自プロトコルのサイズ」の列が追加されます。列のヘッダ部分を右クリックすると、列の一覧が表示するので表示項目の選択が可能です。

f:id:yasuikj:20200312182046p:plain
図7:列に表示する項目の選択

表示フィルタをかける手順

図8に示すように、tree に表示した「独自プロトコルの文字列」を右クリック - [フィルタとして適用] - [選択済]を選択( 必要に応じて好きな項目を選ぶ )すると、表示フィルタ部分に、 original.original_data == "Wooo" と表示し、pinfo部分がフィルタされた内容で表示されます。

f:id:yasuikj:20200312155358p:plain
図8:選択した項目をフィルタとして適用

[ご参考] 自作のDissectorを使わない場合、TCPのパケットであれば、表示フィルタ部に、 tcp.payload contains "Wooo" と入力しても同じような表示は可能です。Dissectorがない場合の参考としてください。

入出力グラフ上でフィルタをかける手順

[統計]-[入出力グラフ] で入出力グラフを表示し、[+]を選択しフィルタを追加してください。Display Fileter 部分に、 表示フィルタに記載する書式と同一書式でoriginal.original_data == "Wooo" と記載することでフィルタ定義を行えます。

これ以上は、Wiresharkの解説になるので、ここでやめておきます。Wiresharkは他にも便利な機能があり、解説したサイトもたくさんあるのでぜひいろいろ試してみてください。

まとめ

本稿では、基本的なDissectorの作り方と、Dissectorを活用したパケット解析方法を紹介しました。

Dissectorはプログラム経験のない方でも比較的容易に作れ、Dissectorを使うことで解析が格段に容易になることを理解いただけたでしょうか。

本稿が独自プロトコルのバイト列と格闘している人達の助けになれば幸いです。

なお、本稿では、 符号なし整数、文字列を題材に説明をしましたが、制御システムのパケットを解析するには、Bit列の解析、エンディアン(Endian)の解析、時刻の解析などもDissectorで解析したい場合があります。これらについてはこちら↓で公開しています。
io.cyberdefense.jp


Black Hat USA 2019とDEF CON 27に参加しました

はじめまして。技術部の遠藤です。今年8月に、Black Hat USA 2019とDEF CON 27に参加してきました。両カンファレンスの様子をお伝えしたいと思います。

はじめに

まず初めに、私について簡単な自己紹介です。
サイバーディフェンス研究所が誇る技術部において、私はエンジニアではなくリサーチャーという珍しい存在です。国内外で発生しているサイバー攻撃の事例や政府の政策動向などについて、公開情報を収集し分析しています。いわゆる「OSINT (Open Source Intelligence) 」と呼ばれる業務です。
今回は非エンジニアの方に分かりやすくお伝えしようと思います。

参加報告

Black Hat/DEF CONとは

Black Hatとは、各国の著名な専門家による講演やトレーニングが開催される世界最大規模のサイバーセキュリティカンファレンスです。このうち、設立当初から毎年夏季に米国ラスベガスで開催されているのがBlack Hat USAです。今年は、トレーニングが8月3日から6日、講演 (Briefing) が7日から8日に亘って行われました。講演とトレーニング以外にも、様々なエンジニアが自身で開発したツールを「アーセナル」というコーナーで紹介しています。

f:id:cdi-endo:20191002170211j:plain
Black Hat USA 2019のKeynote

毎年、Black Hat USAに続いて同じくラスベガスで開催されるカンファレンスがDEF CONです。今年は、8月8日から11日まで4つのホテルを使って開催されました。DEF CONでも講演 (Presentation) は行われますが、メインとなるイベントは世界最高峰のハッキング大会"DEF CON CTF"の決勝戦です。この大会には弊社のエンジニアも参加しています。また、DEF CONでは、IoTやICS (産業制御システム) などテーマごとに”Village”と呼ばれる会場に分かれ、テーマに沿ったCTFやツールの紹介等が行われます。

f:id:cdi-endo:20191002171141j:plain
DEF CON 27 CTF決勝戦

世界中から、Black Hat USA 2019には20,200人、DEF CON 27には25,000人の参加者が集いました。

それでは、Black Hat USA 2019とDEF CON 27の中で興味を引いた発表等をひとつずつご紹介します。

Black Hat USA 2019

まず、Victor Murray氏 (Engineering Group Leader, SwRI) による"Legal GNSS Spoofing and its Effects on Autonomous Vehicles(自動運転車に対する合法的なGNSSのなりすましとその効果)"です。

みなさんご存知、GNSSとはGlobal Navigation Satellite Systemsの略で、GPSなどの衛星測位システムです。偽のGPS信号を発信してGPS受信機をだます行為を「GPSスプーフィング」と言います。この発表では、自動運転車が搭載するGPS受信機に対してスプーフィングを行うことで引きおこる影響について実験結果が共有されました。

各国とも電波の使用は法律で規制されており、許可された場合でも周囲への影響を考慮すると、走行中の自動運転車などに対してなりすまし電波を発する実験を行うことは難しいようです。今回SwRIは、電波を外部に漏らさない筐体(ファラデーケージ)を使って小型の実験装置を作り、これを自動運転車の受信機に取り付けてなりすまし電波を発する実験を行いました。

実験の結果、偽の位置情報を送ることで直進中の自動運転車は進路が変更され、不正な速度 (Velocity) 情報を送ることで旋回中の自動運転車は脱輪するなどしたそうです。

今年7月には一部の報道が、英国のタンカーを拿捕するために、イランがGPSスプーフィングを行った可能性があると伝えています。このとき、ロシア製の技術が利用されたのではないかと疑われています。ロシアと言えば、重要人物や施設の保護、NATOへの対抗等を目的に、ロシア国内外でGPSスプーフィングを行っていることが報告されています。2017年には、米国海事局 (MARAD) が、黒海において大規模なGPSスプーフィングが行われたとして警告 (MSCI Alert) を発表しています。

日本は、GPSを補完する独自の衛星測位システム「みちびき」を開発しており、現在4基の人工衛星で運用しています。2023年度を目途に7基体制を目指しており、自動運転やドローンによる即時配送サービス等へ活用される見込みです。そのため、今回の発表内容は気になるところです。

SwRIは、この成果の共有によって、なりすまし電波に対してGNSS受信機が持つ脆弱性の研究が加速することを期待しているとのことです。

DEF CON 27

DEF CONでご紹介したいのは、SE Villageで開催されたSECTFです。

SEとはSocial Engineerの略で、SE Villageはソーシャルエンジニアリングをテーマとする各種イベントが開催される会場です。そして、SECTFとはSE(ソーシャルエンジニアリング)をテーマとしたハッキング大会 (CTF) です。

一般的なハッキングがシステムの脆弱性を突いて攻撃するのに対して、ソーシャルエンジニアリングは人間の心理を衝いた攻撃です。たとえば、相手を騙して不正なWebサイトに誘導したり、機密情報を聞き出したりする攻撃がこれにあたります。他には、従業員の後ろについて立ち入り禁止区域に侵入したり、ごみ箱から機密文書を漁る行為もソーシャルエンジニアリングです。

SECTFは、インターネットと電話を駆使して、ターゲット企業に関して多くの情報を収集する競技です。集める情報は、利用している清掃業者や従業員の就業時間、OSの名前とバージョンなど攻撃に悪用されうる情報です。参加者はこれらの情報を、DEF CON開催前の2週間にインターネットを駆使して収集し、DEF CON期間中の持ち時間20分では電話を使って巧みにさらなる情報を聞き出します。私が観戦したときには、女性がインターンの申し込みを装って、ターゲット企業が利用しているシステムの情報を聞き出していました。

f:id:cdi-endo:20191002173309j:plain
SECTFの様子

悪意のあるハッカーは、ソーシャルエンジニアリングとハッキングを組み合わせて攻撃してきます。最近の調査では、メールを使ったサイバー攻撃の99%は、不正なリンクをクリックさせたり、添付ファイルを開かせるなど被害者の操作を必要としており、ソーシャルエンジニアリングを仕掛けてきていることを示しています。

実際に、ハマスが若い女性のプロフィールを悪用し、Facebookを通じてイスラエル軍兵士と連絡を取り、出会い系アプリを装ったスパイウェアをダウンロードさせる事案や、退役米軍人用の偽の求人サイトを作って求人アプリを装ったマルウェアを配布する事案などが確認されています。

おわりに

歴史のある大きなカンファレンスなだけあって、講演や各種イベントの内容は洗練されていました。日々の情報収集では、どうしても自分の関心や業務に関係のある情報に目を向けがちになります。最新の情報を、その分野の専門家から直接得られるだけでなく、多面的な情報を半ば強制的に収集するためにも、外部のカンファレンスやセミナーに参加することはやっぱり有意義だと感じました。

Black HatもDEF CONも、「ハッカーの祭典」と呼ばれるだけあって、技術的に高度な発表やイベントが開催されます。エンジニアの皆さんには、非常に魅力的なカンファレンスだと思います。

しかし、私のように、非エンジニアの方でも楽しめる講演等はたくさんあります。発表者はその道のプロですので、説明は非常にわかりやすいです。最近では、リスクマネジメントやガバナンス、政策に関する講演も多いです。CTFは、ハッカーが取り組む様子は見ているだけでも刺激的ですし、守る側として意識が高まります。興味のある方は是非、参加してみてください!

Black HatもDEF CONも講演資料は全て公開されていますので、一度覗いてみると面白いですよ。

株式会社サイバーディフェンス研究所 / Cyber Defense Institute Inc.