wiresharkの解析プラグインを作る ssmjp 201409

58
Wireshark の のののののののののの @unkn0wnbit 2014/9/29 #ssmjp 2014/09

Upload: minoru-kobayashi

Post on 28-May-2015

4.050 views

Category:

Internet


0 download

DESCRIPTION

How to make a Wireshark plugin to dissect an original protocol with Lua. And, How to decrypt SSL (using PFS (ex. ECDHE) key exchange) with Wireshark.

TRANSCRIPT

Page 1: Wiresharkの解析プラグインを作る ssmjp 201409

Wireshark の解析プラグインを作る

@unkn0wnbit

2014/9/29 #ssmjp 2014/09

Page 2: Wiresharkの解析プラグインを作る ssmjp 201409

Agenda

Wireshark とは 解析プラグインを作るには 解析対象プロトコルを定義 解析スクリプトの作成 既存の解析プラグインの拡張 ビット演算 おまけ

2

Page 3: Wiresharkの解析プラグインを作る ssmjp 201409

01. WIRESHARK とは

3

Page 4: Wiresharkの解析プラグインを作る ssmjp 201409

4

Wireshark とは

パケットキャプチャツール 標準で多彩なプロトコルを解析できる

プラグインが付属 独自のプロトコルは解析できない

独自のプロトコルを解析する場合1. 人間デコーダになる2. 解析用のプラグインを書く

Page 5: Wiresharkの解析プラグインを作る ssmjp 201409

5

Wireshark のパケット処理の流れ

キャプチャフィルタ

ディスプレイフィルタ

解析プラグイン

Wireshark 画面出力

0100111010101010101110 ネットワーク上を流れるパケット

キャプチャするパケットをフィルタリング

表示するパケットをフィルタリング

パケットを解析

解析した結果を表示

Page 6: Wiresharkの解析プラグインを作る ssmjp 201409

02. 解析プラグインを作るには

6

Page 7: Wiresharkの解析プラグインを作る ssmjp 201409

7

解析プラグインを作るには

方法は 2 つ C/C++ 言語で作成 Wireshark に組み込まれた Lua スクリプ

ト言語で作成

今回は Lua で作成します

面倒そう…

きっとお手軽

Page 8: Wiresharkの解析プラグインを作る ssmjp 201409

8

Lua を使う準備 (1/2)

Wireshark で Lua が有効か確認 コンパイル時に Lua の使用を指定

公式版 Win/Mac バイナリともにデフォルトで有効

Linux はディストリビューションによるかも

http://wiki.wireshark.org/Lua より

Page 9: Wiresharkの解析プラグインを作る ssmjp 201409

9

Lua を使う準備 (2/2)

最新バージョン (1.12.1) はデフォルトで使用可能 古い設定ファイルを使い回している場合は確認

init.lua 内の以下の行を確認 disable_lua = true; の行を false に変更

Windows C:\Program Files\Wireshark\init.lua

Mac /Applications/Wireshark.app/Contents/Resources/share/

wireshark/init.lua Linux (Debian 7)

/usr/share/wireshark/init.lua

Page 10: Wiresharkの解析プラグインを作る ssmjp 201409

10

動作確認 (1/2)

Tools – Lua – Evaluate

Evaluate Lua ダイアログ

Page 11: Wiresharkの解析プラグインを作る ssmjp 201409

11

動作確認 (2/2)

ダイアログにプログラム入力

Evaluate ボタンを押すと…

local tw = TextWindow.new("Test Program");tw:set("Hello World!")

Page 12: Wiresharkの解析プラグインを作る ssmjp 201409

12

Lua スクリプト使用方法 (1/2)

通常はスクリプトをファイルに保存して、 Wireshark の起動時に読み込ませる。

-X オプションは複数回指定可能 ディレクトリのデリミタは “ \” or “/” tshark も同様( GUI の API は使えない)

tshark 版 Hello World

wireshark.exe -X lua_script:C:/wireshark_plugins/hoge.lua

print("Hello World!")

Page 13: Wiresharkの解析プラグインを作る ssmjp 201409

13

Lua スクリプト使用方法 (2/2)

毎回同じスクリプトを使う場合 init.lua に以下を追記

dofile(”C:/wireshark_plugins/hoge.lua”) Global configuration

C:\Program Files\Wireshark\init.lua Personal configuration

C:\Users\<user_name>\AppData\Roaming\Wireshark\init.lua

各フォルダの確認方法 Help – About Wireshark – Folders

Page 14: Wiresharkの解析プラグインを作る ssmjp 201409

03. 解析対象プロトコルを定義

14

Page 15: Wiresharkの解析プラグインを作る ssmjp 201409

15

解析対象プロトコルを定義 (1/4)

解析対象とする簡単なプロトコルを定義 乱数文字列生成プロトコル

クライアントはサーバに任意の長さの乱数を要求する

サーバは指定された長さの乱数を文字列として、クライアントに返す

Page 16: Wiresharkの解析プラグインを作る ssmjp 201409

16

解析対象プロトコルを定義 (2/4)

パケットフォーマット Command Code

データ長: 1 バイト Request or Response

Data Length データ長: 2 バイト Request 時:要求するデータ長を指定 Response 時: Data 部の長さを指定

Data データ長: Data Length で指定されたバイト数 Request 時:存在しない Response 時:応答する実データ

データの並びはビッグエンディアン

8 1Command Code

Data Length

bits

Data

Page 17: Wiresharkの解析プラグインを作る ssmjp 201409

17

解析対象プロトコルを定義 (3/4)

Command Code Request

0x01 Response

0x51 不明な Command Code が要求されたと

き 0xFF を返す

Page 18: Wiresharkの解析プラグインを作る ssmjp 201409

18

解析対象プロトコルを定義 (4/4)

UDP 版および TCP 版を実装 MTU を超えるデータをやりとりする場

合、 TCP の使用を想定。 使用ポート番号: 10000

Page 19: Wiresharkの解析プラグインを作る ssmjp 201409

04. 解析スクリプトの作成

19

Page 20: Wiresharkの解析プラグインを作る ssmjp 201409

20

Wireshark で見てみる( UDP版)

Response

Packet List

Packet Bytes

Packet Details

Page 21: Wiresharkの解析プラグインを作る ssmjp 201409

21

解析スクリプト( UDP 版)do udp_rngp_proto = Proto("RNGP_UDP", "Random Number Generater Protocol (UDP)")

command_prtf = ProtoField.new("RNGP command", "rngp_udp.command", ftypes.UINT8) length_prtf = ProtoField.new("RNGP length", "rngp_udp.length", ftypes.UINT16) random_prtf = ProtoField.new("RNGP Random Numbers", "rngp_udp.random", ftypes.STRING) udp_rngp_proto.fields = {command_prtf, length_prtf, random_prtf}

function udp_rngp_proto.dissector(buffer, pinfo, tree) local command_names = { [0x01] = "Request Random Numbers", [0x51] = "Response Random Numbers", [0xFF] = "Unknown Request/Response Command", }

local command = buffer(0,1):uint() local length = buffer(1,2):uint()

local subtree = tree:add(udp_rngp_proto, "Random Number Generater Protocol Data") subtree:add_packet_field(command_prtf, buffer:range(0,1), ENC_ASCII, "Command:", string.format("0x%02x", command), command_names[command]) subtree:add_packet_field(length_prtf, buffer(1,2), ENC_ASCII, "Length:", length)

if command >= 0x51 and command ~= 0xFF then disp_data(command, buffer(3, length):tvb(), subtree) end

pinfo.cols.protocol = "RNGP(UDP)" if command_names[command] == nil then pinfo.cols.info = "Malformed Request/Response Command" else pinfo.cols.info = command_names[command] end end

udp_table = DissectorTable.get("udp.port") udp_table:add(10000, udp_rngp_proto)end

function disp_data(command, buffer, tree) if command == 0x51 then tree:add_packet_field(random_prtf, buffer(0), ENC_ASCII) endend

新しいプロトコルを宣言

定義した dissector を10000/udp に登録

buffer : Wireshark から渡されるパケットのバッファ(tvb)pinfo : Packet List 情報tree : Packet Details 内ツリー情報

buffer(X, Y):uint()buffer の X バイト目から Y バイトを unsigned int として切り出す

tree:add(), add_packet_field()Packet Details 内のツリーにアイテムを追加

Packet List の Protocol カラムと Info カラムの内容を設定

dissector (パケットを解析する関数)を定義

(パケットを受け取るたびに呼び出される)

宣言したプロトコルで使用するフィールドを定義

Page 22: Wiresharkの解析プラグインを作る ssmjp 201409

22

解析スクリプト( UDP 版)

Response

Page 23: Wiresharkの解析プラグインを作る ssmjp 201409

23

Wireshark で見てみる( TCP版)

Response

Page 24: Wiresharkの解析プラグインを作る ssmjp 201409

24

解析スクリプト( TCP 版)do tcp_rngp_proto = Proto("RNGP_TCP", "Random Number Generater Protocol (TCP)")

command_prtf = ProtoField.new("RNGP command", "rngp_tcp.command", ftypes.UINT8) length_prtf = ProtoField.new("RNGP length", "rngp_tcp.length", ftypes.UINT16) random_prtf = ProtoField.new("RNGP Random Numbers", "rngp_tcp.random", ftypes.STRING) tcp_rngp_proto.fields = {command_prtf, length_prtf, random_prtf}

function tcp_rngp_proto.dissector(buffer, pinfo, tree) local command_names = { [0x01] = "Request Random Numbers", [0x51] = "Response Random Numbers", [0xFF] = "Unknown Request/Response Command", }

local command = buffer(0,1):uint() local length = buffer(1,2):uint()

if command == 0x51 and buffer:len() < (3 + length) then pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT return buffer:len() - (3 + length) end

local subtree = tree:add(tcp_rngp_proto, "Random Number Generater Protocol Data") subtree:add_packet_field(command_prtf, buffer:range(0,1), ENC_ASCII, "Command:", string.format("0x%02x", command), command_names[command]) subtree:add_packet_field(length_prtf, buffer(1,2), ENC_ASCII, "Length:", length)

if command >= 0x51 and command ~= 0xFF then disp_data(command, buffer(3, length):tvb(), subtree) end

pinfo.cols.protocol = "RNGP(TCP)" if command_names[command] == nil then pinfo.cols.info = "Malformed Request/Response Command" else pinfo.cols.info = command_names[command] end

return 3 + length end

tcp_table = DissectorTable.get("tcp.port") tcp_table:add(10000, tcp_rngp_proto)End

function disp_data(command, buffer, tree) if command == 0x51 then tree:add_packet_field(random_prtf, buffer(0), ENC_ASCII) endend

新しいプロトコルを宣言

dissector を定義

定義した dissector を10000/tcp に登録

buffer のデータ長がプロトコルで指定された長さよりも短い場合、必要なデータ長になるまで処理を行わない。次に dissector が呼び出される際、 bufferに後続のパケットのペイロードがアペンドされる。

TCP セグメント再構築

宣言したプロトコルで使用するフィールドを定義

Page 25: Wiresharkの解析プラグインを作る ssmjp 201409

25

解析スクリプト( TCP 版)

TCP セグメント再構築イメージ

1 パケット目 2 パケット目

1 パケット目で dissector に渡される buffer 内容必要なデータ長に満たないので、 pinfo.desegment_len にDESEGMENT_ONE_MORE_SEGMENT をセットして、dissector から return する。

サーバが返すデータ

2 パケット目で dissector に渡される buffer 内容

Wireshark は DESEGMENT_ONE_MORE_SEGMENT がセットされている場合、次のパケットのペイロードを buffer にアペンドして dissector に渡す。必要なデータ長か否かは dissector が判断する。

Page 26: Wiresharkの解析プラグインを作る ssmjp 201409

26

解析スクリプト( TCP 版)

Response

Page 27: Wiresharkの解析プラグインを作る ssmjp 201409

05. 既存の解析プラグインの拡張

27

Page 28: Wiresharkの解析プラグインを作る ssmjp 201409

28

ところで…

独自のプロトコルの解析より、既存の解析プラグインを拡張したい、と思うことの方が多いかもしれません。

この機能が使えます。 postdissector chained dissector TAP

Page 29: Wiresharkの解析プラグインを作る ssmjp 201409

29

postdissector

postdissector とは dissector が呼び出された後に呼び出され

る dissector 同じパケットを複数の dissector で処理する。 種類に関係なく、全てのパケットに対して

呼び出される。 通常の dissector と同様にパケットの情

報へのアクセスや Packet Details のツリーにアイテムを追加できる

Page 30: Wiresharkの解析プラグインを作る ssmjp 201409

30

postdissector のイメージ

dissector

postdissector

ARP IP

TCP UDP

25 80 53 68 …

各 dissector で処理が行われた後に必ず呼び出される dissector

Page 31: Wiresharkの解析プラグインを作る ssmjp 201409

31

postdissector 例

http://wiki.wireshark.org/Lua/Dissectors より

-- trivial postdissector example-- declare some Fields to be readip_src_f = Field.new("ip.src")ip_dst_f = Field.new("ip.dst")tcp_src_f = Field.new("tcp.srcport")tcp_dst_f = Field.new("tcp.dstport")-- declare our (pseudo) protocoltrivial_proto = Proto("trivial","Trivial Postdissector")-- create the fields for our "protocol"src_F = ProtoField.string("trivial.src","Source")dst_F = ProtoField.string("trivial.dst","Destination")conv_F = ProtoField.string("trivial.conv","Conversation","A Conversation")-- add the field to the protocoltrivial_proto.fields = {src_F, dst_F, conv_F}-- create a function to "postdissect" each framefunction trivial_proto.dissector(buffer,pinfo,tree) -- obtain the current values the protocol fields local tcp_src = tcp_src_f() local tcp_dst = tcp_dst_f() local ip_src = ip_src_f() local ip_dst = ip_dst_f() if tcp_src then local subtree = tree:add(trivial_proto,"Trivial Protocol Data") local src = tostring(ip_src) .. ":" .. tostring(tcp_src) local dst = tostring(ip_dst) .. ":" .. tostring(tcp_dst) local conv = src .. "->" .. dst subtree:add(src_F,src) subtree:add(dst_F,dst) subtree:add(conv_F,conv) endend-- register our protocol as a postdissectorregister_postdissector(trivial_proto)

パケット内で読み取るフィールドを宣言

新しいプロトコルを宣言

宣言したプロトコルで使用するフィールドを定義

postdissector を定義

postdissector を登録

TCP の場合に送信元/先 IP アドレスとポートの組み合わせを Packet Details に追加。

Page 32: Wiresharkの解析プラグインを作る ssmjp 201409

32

postdissector 例実行結果

定義したプロトコルやフィールドはディスプレイフィルタとして指定できる。

Packet Details にも情報を追加できる。この例では、 TCP の場合のみ Trivial Protocol Data ツリーが追加される。

Page 33: Wiresharkの解析プラグインを作る ssmjp 201409

33

chained dissector

chained dissector とは ある dissector が呼び出された後に続け

て呼び出される dissector 同じパケットを複数の dissector で処理する 特定のプロトコルに関してのみ呼び出され

る 通常の dissector と同様にパケットの情

報へのアクセスや Packet Details のツリーにアイテムを追加できる

Page 34: Wiresharkの解析プラグインを作る ssmjp 201409

34

chained dissector のイメージ

dissector

chaineddissector

ARP IP

TCP UDP

25 80 53 68 …

特定の dissector に紐付けられて呼び出される dissector

Page 35: Wiresharkの解析プラグインを作る ssmjp 201409

35

chained dissector 例do local http_suspicious_proto = Proto("http_suspicious", "Suspicious HTTP Traffic")

local F_suspicious_uri = ProtoField.string("http.suspicious_uri", "Suspicious Request URI") local F_suspicious_host = ProtoField.string("http.suspicious_host", "Suspicious Host Header") http_suspicious_proto.fields = {F_suspicious_uri, F_suspicious_host}

local f_request_uri = Field.new("http.request.uri") local f_host = Field.new("http.host") local original_http_dissector

function http_suspicious_proto.dissector(buffer, pinfo, tree) original_http_dissector:call(buffer, pinfo, tree)

if f_request_uri() then local uri = tostring(f_request_uri()) local host = tostring(f_host())

if string.match(uri, "%.exe") and string.match(host, "[^j][^p]:%d+") then local subtree = tree:add(http_suspicious_proto, buffer) subtree:add(F_suspicious_uri, buffer(), uri) :set_text("URI : " .. uri) subtree:add(F_suspicious_host, buffer(), host) :set_text("Host : " .. host) end end end

local tcp_dissector_table = DissectorTable.get("tcp.port") original_http_dissector = tcp_dissector_table:get_dissector(8080) tcp_dissector_table:add(8080, http_suspicious_proto)end

新しいプロトコルを宣言

宣言したプロトコルで使用するフィールドを定義

パケット内で読み取るフィールドを宣言

chained dissector を定義

chained dissector を登録

chained dissector の処理を行う前に、オリジナルの dissector で処理を行う。

8080/tcp に登録されているオリジナルの dissector をバックアップ。

リクエストされている URI と Hostヘッダから疑わしいか判断。

Page 36: Wiresharkの解析プラグインを作る ssmjp 201409

36

chained dissector 例実行結果

Packet Details にも情報を追加できる。この例では、疑わしい HTTP のリクエストに Suspicious HTTP Traffic ツリーが追加される。

定義したプロトコルやフィールドはディスプレイフィルタとして指定できる。

Page 37: Wiresharkの解析プラグインを作る ssmjp 201409

37

TAP

TAP とは 主に統計情報収集用として使用される。 全てのパケットに対して呼び出される。

フィルタを設定して、該当するパケットのみを処理することも可能。 キャプチャフィルタの影響は受ける。 ディスプレイフィルタの影響は受けない。

dissector とは異なり、 Packet Details のツリーにアイテムは追加できない。

Page 38: Wiresharkの解析プラグインを作る ssmjp 201409

38

TAP のイメージ

キャプチャフィルタ

ディスプレイフィルタ

解析プラグイン

Wireshark 画面出力

0100111010101010101110

TAP

dissector とは独立してパケットを解析。

Page 39: Wiresharkの解析プラグインを作る ssmjp 201409

39

TAP 例do local function menuable_tap() -- Declare the window we will use local tw = TextWindow.new("Address Counter")

-- This will contain a hash of counters of appearances of a certain address local ips = {}

-- this is our tap local tap = Listener.new();

function remove() -- this way we remove the listener than otherwise will remain running indifinitelly tap:remove(); end

-- we tell the window to call the remove() function when closed tw:set_atclose(remove)

-- this function will be called once for each packet function tap.packet(pinfo,tvb) local src = ips[tostring(pinfo.src)] or 0 local dst = ips[tostring(pinfo.dst)] or 0

ips[tostring(pinfo.src)] = src + 1 ips[tostring(pinfo.dst)] = dst + 1 end

-- this function will be called once every few seconds to update our window function tap.draw(t) tw:clear() for ip,num in pairs(ips) do tw:append(ip .. "\t" .. num .. "\n"); end end

-- this function will be called whenever a reset is needed -- e.g. when reloading the capture file function tap.reset() tw:clear() ips = {} end end

-- using this function we register our fuction -- to be called when the user selects the Tools->Test->Packets menu register_menu("Test/Packets", menuable_tap, MENU_TOOLS_UNSORTED)end http://www.wireshark.org/docs/wsug_html_chunked/wslua_tap_example.html より

TAP を生成

パケットを受け取るたびに

呼び出される関数

テキストウィンドウに結果を表示

Tools メニューにスクリプトを実行するメニュー

を追加

Listener.new(“frame”, “ip.addr == 10.0.0.0/8”) のようにフィルタリングをすることも可能。

送信元または送信先ごとにパケット数をカウント

Page 40: Wiresharkの解析プラグインを作る ssmjp 201409

40

TAP 例実行結果

Tools – Test – Packets が追加される。

メニューを実行して、パケットキャプチャを行うと、送信元または送信先ごとのパケット数がウィンドウに表示される。

Wireshark の名前解決を有効にすると、ドメイン名などが解決された結果で集計される。

Page 41: Wiresharkの解析プラグインを作る ssmjp 201409

06. ビット演算

41

Page 42: Wiresharkの解析プラグインを作る ssmjp 201409

42

Lua のビット演算

Lua 5.2 でビット演算がサポート 公式版 Windows バイナリは Lua 5.2 組み込み 公式版 Mac バイナリは Lua 5.1 組み込み

いずれも、 Wireshark 1.12.1 で確認 Linux はディストリビューションによるかも

Debian 7 では、 Wireshark 1.8.2 のパッケージ (Lua 5.1) Lua 5.1 では外部ライブラリを使えば可能

bitop : http://bitop.luajit.org/ Lua 5.1/5.2 用 使うならばこちらがお勧めだが、 Lua 5.2 と API が異なる Debian 7 ではパッケージが用意されている (lua-bitop)

bitlib : https://github.com/LuaDist/bitlib Lua 5.1 用 もうメンテナンスされていないっぽい

Page 43: Wiresharkの解析プラグインを作る ssmjp 201409

Lua ビット演算

API bit32.arshift, bit32.band, bit32.bnot, bit32.bor, bit32.btest,

bit32.bxor, bit32.bextract, bit32.lrotate, bit32.lshift, bit32.replace, bit32.rrotate, bit32.rshift

詳細: http://www.lua.org/manual/5.2/manual.html#6.7 “0x1234” と “ 0xFF00” の AND の動作確認

Tools – Lua – Evaluate で以下を入力

43

local tw = TextWindow.new("BitOp Test Program");band_val = bit32.band(0x1234, 0xFF00)tw:set("lua version: " .. _VERSION .."\n" .. band_val )

Page 44: Wiresharkの解析プラグインを作る ssmjp 201409

44

まとめ パケット単位の解析

今まさにキャプチャしたパケットのみが解析対象 過去のパケットの情報は取得できない

1 つのプロトコルにつき、 1 つの dissector 単一の dissector で Request と Response を解析 chained dissector などで既存の dissector の拡張は可能

Wireshark で Lua を使うにはコンパイル時に指定する必要あり 公式配布バイナリ (Win/Mac) 版は標準で組み込み済み

Windows バイナリ: Lua 5.2 Mac バイナリ: Lua 5.1

Linux 版はディストリビューションによって異なる可能性あり Lua 5.2未満の場合はビット演算ができない

外部ライブラリ (bitop) を使えば可能 ただし、本家 Lua と API が異なる

Page 45: Wiresharkの解析プラグインを作る ssmjp 201409

45

参考 Wireshark

Wireshark User’s Guide https://www.wireshark.org/docs/wsug_html_chunked/wsluarm.html https://www.wireshark.org/docs/wsug_html_chunked/lua_module_Proto.html#lua_class_ProtoField https://www.wireshark.org/docs/wsug_html_chunked/lua_module_Tree.html

The Wireshark Wiki http://wiki.wireshark.org/Lua http://wiki.wireshark.org/Lua/Dissectors http://wiki.wireshark.org/Lua/Examples http://wiki.wireshark.org/Lua/Taps

Wireshark 1.2.9 ソースコードアーカイブ内 README.developer README.tapping

Lua http://www.lua.org/manual/5.2/manual.html#6.7

その他 Googleブックス (http://books.google.co.jp/books)

Wireshark and Ethereal network protocol analyzer toolkit 実践パケット解析 : Wireshark を使ったトラブルシューティング

http://www.lua.org/ http://bitop.luajit.org/ http://luaforge.net/projects/bitlib/

Page 46: Wiresharkの解析プラグインを作る ssmjp 201409

46

Q & A

Page 47: Wiresharkの解析プラグインを作る ssmjp 201409

おまけ

Lua 以外のバインディング言語 SSL復号

サーバの証明書を使う ブラウザの暗号鍵を使う

47

今回やりません。

ググって!

Page 48: Wiresharkの解析プラグインを作る ssmjp 201409

おまけ 1Lua 以外のバインディング

48

Page 49: Wiresharkの解析プラグインを作る ssmjp 201409

Lua 以外のバインディング言語

About ダイアログをよくみると…

49

Page 50: Wiresharkの解析プラグインを作る ssmjp 201409

Python バインディングの実際のところ

without なので Wireshark wiki を見てみる

50

http://wiki.wireshark.org/Python より

Page 51: Wiresharkの解析プラグインを作る ssmjp 201409

pyreshark について

Lua のように TCP や UDP のポート毎に dissector を登録するような API が用意されていない。 tcp_table = DissectorTable.get("tcp.port") tcp_table:add(10000, tcp_rngp_proto)

TCP/IPヘッダを自力でゴリゴリ解析する必要がありそう。

面倒そうなので試すのをやめました…。 51

Page 52: Wiresharkの解析プラグインを作る ssmjp 201409

おまけ 2ブラウザ側の暗号鍵を使った SSL復号

52

Page 53: Wiresharkの解析プラグインを作る ssmjp 201409

Wireshark で SSL復号

よく見かける方法として、サーバの SSL秘密鍵をWireshark に設定して、パケットキャプチャの解析を行う手順が解説されている。

自分が SSL秘密鍵を持っていない場合、通信内容が分からない! 外部のサービスを使う場合とか Fiddler とか使えば見えますが

しかし、 SSL秘密鍵がなくても、ブラウザ側が持っている暗号鍵で SSL復号を行うことができる。

53

Page 54: Wiresharkの解析プラグインを作る ssmjp 201409

ブラウザの暗号鍵で復号

SSLKEYLOGFILE環境変数を設定する SSLKEYLOGFILE=/path/to/sslkeylog.txt

Wireshark で以下を設定 Edit – Preferences – Protocols – SSL – (Pre)-Master-

Secret log filename display filter で “ ssl” を設定

適当なパケットを選択して、 Follow SSL Stream Packet Bytes の Decrypted SSL data タブ等

鍵交換プロトコルが RSA でも ECDHE(Perfect Forward Secrecy) でも復号可能 PFSだとサーバの SSL秘密鍵を持っていても復号できな

い 54

Page 55: Wiresharkの解析プラグインを作る ssmjp 201409

SSLKEYLOGFILE フォーマット

CLIENT_RANDOM <space> <64 bytes of hex encoded client_random> <space> <96 bytes of hex encoded master secret>

RSA <space> <16 bytes of hex encoded encrypted pre master secret> <space> <96 bytes of hex encoded pre master secret>

55

Page 56: Wiresharkの解析プラグインを作る ssmjp 201409

使用ブラウザの制限

SSLKEYLOGFILE環境変数を見てログファイルを作るのは、 NSS(Network Security Services) ライブラリであるため、同様の手順が取れるのは同ライブラリを使用するブラウザ、ツールのみ。

NSS を使用する主なブラウザは、 Firefox, PC 版Chrome ( Win/Mac/Linux 版含む)のみ。 Android 版 Chrome は OpenSSL を使用 PC 版 Chrome も BoringSSL を使い始めた場合は使えな

くない 他のブラウザの場合はメモリダンプするくらいし

か手がないんじゃ…。56

Page 57: Wiresharkの解析プラグインを作る ssmjp 201409

参考

pyreshark https://github.com/ashdnazg/pyreshark

Psst. Your Browser Knows All Your Secrets. https://isc.sans.edu/diary/Psst.+Your+Browser+Knows+All+Your+Secrets./16415

NSS Key Log Format https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format

Chrome: From NSS to OpenSSL https://docs.google.com/document/d/1ML11ZyyMpnAr6clIAwWrXD53pQgNR-

DppMYwt9XvE6s/edit?pli=1#heading=h.n30fi956cpfk SSL/TLS & Perfect Forward Secrecy

http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html

ディフィー・ヘルマン鍵共有 http://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%83%95%E3%82%A3%E3%83%BC

%E3%83%BB%E3%83%98%E3%83%AB%E3%83%9E%E3%83%B3%E9%8D%B5%E5%85%B1%E6%9C%89

Diffie–Hellman key exchange http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange

57

Page 58: Wiresharkの解析プラグインを作る ssmjp 201409

58

Q & A