地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Linux】tcpdumpを使って通信しているパケットのtlsバージョンを調べるメモ

背景

提供しているサービスに対してクライアントがどのTLSバージョンで来ているのかを調べるみたいな場合にサーバのログで出せれば一番手っ取り早いですが「そんな機能ないよ!」みたいな場合も往々にしてあると思い、「その場合はどうするのが良いんだろう?」と思って調べた記事です。そんなケースがあるのかは分からないですが勢いで調べてみました。(絶対のログの形式を変えてはいけないみたいなケースとかあるのかな。)

結論だけ書くとtcpdumpした結果をうまくawkなりでなんなりでパースすればいい感じにバージョンの統計データが取れそうでした。やりたくはないですがw

実験

TLS通信で使われているバージョンをtcpdumpを使って調べるための方法。打つコマンドは以下。

今回やりたいのクライアントが使用してくるTLSのバージョンを知りたいのでtcpdumpではTLSハンドシェイクに絞って実行していく。tcpヘッダー直後の1バイトが22(10)であるパケットのみを表示するコマンド。

$ tcpdump -ni ens192 "tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)" -x -s0

下記の記事にある通りContent Type:22はHandshakeとなっている。これでフィルタリングしていく。

milestone-of-se.nesuke.com

別コンソールを開いて適当にhttpsで通信を行う。すると以下のような出力が得られる。

 0x0000:  4500 00fd fce3 4000 4006 2806 c0a8 010a
    0x0010:  8efa c464 8afc 01bb 8dbf 8df8 df9e 5e04
    0x0020:  8018 00e5 1601 0000 0101 080a 0500 7166
    0x0030:  1f87 be9d 1603 0100 c401 0000 c003 0362
    0x0040:  fe05 ef7f e114 c927 d16f 84e4 d206 9f7c
    0x0050:  1583 4aaf 1f3a df90 bde1 0c3c 01ff 0c00
    0x0060:  0006 1301 1303 1302 0100 0091 0000 0013
    0x0070:  0011 0000 0e77 7777 2e67 6f6f 676c 652e
    0x0080:  636f 6d00 1700 00ff 0100 0100 000a 0014
    0x0090:  0012 001d 0017 0018 0019 0100 0101 0102
    0x00a0:  0103 0104 0033 0026 0024 001d 0020 1281
    0x00b0:  3328 2ad9 9727 eae0 cc82 cf03 84f8 dc70
    0x00c0:  32e0 f6c8 de60 4fde 2822 f09e de02 002b
    0x00d0:  0003 0203 0400 0d00 1800 1604 0305 0306
    0x00e0:  0302 0308 0408 0508 0604 0105 0106 0102
    0x00f0:  0100 2d00 0201 0100 1c00 0240 01

以下はIP/TCPヘッダーなので不要。

 0x0000:  4580 05ae 0646 0000 3806 e874 acd9 1f84
    0x0010:  c0a8 010a 01bb bb5c 228a 848c a6ec 74c9
    0x0020:  8010 0105 eab8 0000 0101 080a 9d78 e84c
    0x0030:  04ec 79b6

5バイト目が16(16)なのでhandshakeとなっている。先程のサイトを参考にすると6~7バイトの4bitがtlsのバージョンとなっている

 0x0030:  04ec 79b6 1603 0300 5a02 0000 5603 03b9
----------------------^^^^

今回は何も指定せずにcurlを実行しているので0x0303となっているようでtls1.2での通信を要求したようです。

tools.ietf.org

// 各バージョンでの実装はこのようになっているらしい
0x0300; / * SSL v1 * /
0x0301; / * TLS v1 * /
0x0302; / * TLS v1.1 * /
0x0303; / * TLS v1.2 * /

opensslなりcurlなりでバージョンを変えていくとここの数字が変わっていくのがみれます。

# ssl1
0x0030:  d089 475e 1603 0000 8f01 0000 8b03 0028

# tls1
0x0030:  050e c5f9 1603 0100 ba04 0000 b600 0001

# tls1.1
0x0030:  0511 91bc 1603 0200 ba04 0000 b600 0001

# tls1.2
0x0030:  050f 8c0f 1603 0300 ba04 0000 b600 0001

途中で気づいたのですがtcpdumpのフィルターでClientHelloメッセージ以外にしないとClientHelloメッセージのレコードレイヤーバージョンは、下位互換性のためにTLSv 1.0を使用するので要件次第では意図した結果にはならなそうです。

tls1.3だとどうなるのか

tls1.3だとどうなるのかの記述が見つけられなかったので試してみようとしました。そしたらmacだと以下のようになってそのままでは使えなそうでした。

curl https://cafe.classmethod.jp/ --tlsv1.3
curl: (4) LibreSSL was built without TLS 1.3 support

以下の記事を参考にTLSv 1.3 に対応している curlを導入してみます。

dev.classmethod.jp

実行。位置は若干ずれてますが"03 03"になっているのがわかります。tls1.3なら0x0304じゃないの?って思ったのですがどうやら違うようです。

0x0040:  3858 1603 0300 7a02 0000 7603 037a 7937

下位互換性を考慮した作りとなっているようなことが書かれていました。通信時にはTLS v1.2 を示す 0x0303 を格納して拡張領域にTLS v1.3であることを伝え認識できるサーバであれば移行をTLS v1.3で通信を行うような仕組みのようです(この辺全然勉強してないので自信ないので今度調べる。)

tools.ietf.org

wiresharkを見ると以下のようになっていました。

f:id:ryuichi1208:20210905234436p:plain

Extension: supported_versions は TLS v1.3 対応のサーバしか理解できなとのこと。なるほどね〜。

参考