通过 Wireshark 解析 TLS 报文

2023-07-16 linux

SSL/TLS 是目前使用最广泛的安全通讯协议,最常见的 HTTPS 也是建立在该协议之上的,尤其是 TLSv1.3 版本更新之后,其性能、安全性得以有效提升,在带来安全性的同时,在开发阶段很难对通讯协议进行调试。

通过 Wireshark 可以分析绝大部分的 TCP/IP 协议流量,包括了 TLS 协议,可以用来分析协议的工作方式或者排查问题。

简介

可以直接在本地通过 Wireshark 解析报文,不过对于需要分析服务器上的报文的话,建议还是通过 tcpdump 捕获报文,然后再通过 Wireshark 进行分析,这里也是使用这一策略。

注意,TLSv1.3 与 TLSv1.2 的协议差距很大,所以需要确保对应的 Wireshark 版本支持,官方没有明确指出那个版本开始支持,不过基本上 2017.01 之后的版本都会支持,所以,使用最新的即可。

原理解析

解密的核心过程是获得 SessionKey (MasterSecrte),也就是在密钥交换过程中,如何通过各种元素获取到会话中的密钥。

SessionKey 是通过 ClientRandom ServerRandom 以及 PreMasterSecret 生成,其中,最为关键是 PreMasterSecret 的获取,RSA 是由客户端生成,并将其用服务端的公钥加密,然后发送。

而 DH 算法则是由客户端和服务端各自生成,然后不通过网络传输完成信息交换。

RSA

如果是通过 RSA 算法交换秘钥,那么客户端会加密并发送给服务端,这样只需要知道服务端的私钥就可以解密出 PreMasterSecret 值。

DH

而 DH 算法中的 PreMasterSecret 是由服务端和客户端各自计算出来,而且没有保存到磁盘,没有通过网络传输,这样就导致无法进行解密。

如果要解密 DH 只能通过类似中间人攻击的方法,或者在浏览器中将 PreMasterSecret 导出。

使用方法

那么在解析的时候就有几种方法,对于 RSA 来说,可以直接指定服务端的密钥,但是前提是要求可以获取到密钥,通常是在自己调试时,否则基本不可用。

可以直接将会话密钥保存起来使用,这种方式就比较通用了,浏览器 Firefox 和 Chrome 都支持 NSS Key Log Format 格式,只需要在启动前设置好 SSLKEYLOGFILE 环境变量。

还可以使用中间人的方式,常见的工具如 mitmproxy、Fiddler 。

指定算法

所以在测试时可以使用 RSA 算法,这样方便对通讯过程数据解密进行分析,如果一切正常,那么就可以自由使用相关的算法。

openssl ciphers -V | column -t

注意,查看上述 Kx=XXX 中的内容,需要保证是 RSA 算法。

示例

测试时使用 OpenSSL 提供的命令行作为简单示例,关于证书的生成过程可以参考 OpenSSL 常用命令 中的相关介绍。

相关的常用命令有。

----- 抓取如下示例生成的报文,用于WireShark分析
tcpdump -i lo -w /tmp/tls.cap -nnnX port 44330

----- 查看私钥信息,如果无密码可以忽略最后的参数
openssl rsa -in private.pem -noout -text -passin pass:123456

通过如下命令启动服务端,客户端可以使用不同的场景。

----- 服务端
openssl s_server -accept 44330 -CAfile pki/CA/cacert.pem            \
	-key pki/SVR/key.pem -cert pki/SVR/cert.pem -state

----- 客户端,使用TLSv1.2版本,并指定RSA加密套件
openssl s_client -connect 127.0.0.1:44330 -CAfile pki/CA/cacert.pem \
	-key pki/CLI/key.pem -cert pki/CLI/cert.pem -state          \
	-cipher AES128-SHA256 -tls1_2

----- 客户端,使用TLSv1.3版本,并记录Master Secret
openssl s_client -connect 127.0.0.1:44330 -CAfile pki/CA/cacert.pem \
	-key pki/CLI/key.pem -cert pki/CLI/cert.pem -state          \
	-tls1_3 -keylogfile key.log

在测试时,建立链接后,通过客户端发送 Hello World! 字符串,然后直接退出。

RSA

简单来说,就是通过指定服务端的私钥以及通讯的内容,对报文进行解密。

如上示例,也就是使用 TLSv1.2 版本以及 AES128-SHA256 加密套件,可以使用 tlsv1.2_rsa.cap 测试。其实在指定了 -cipher AES128-SHA256 参数之后,其中的 -tls1_2 参数可以省略。

在 WireShark 中,通过 [Edit]->[Preference]->[Protocols]->[SSL] 设置 RSA keys list 选项即可,也就是上述的 pki/SVR/key.pem 文件,内容如下。

TLS WireShark RSA Decrypt Example

其中的 Protocol 可以不用指定,如果对私钥进行了加密,那么可以同时设置密码。

然后再打开上述抓包文件,双击 Application Data 中的内容,在弹出的窗口中查看 Decrypted SSL 标签页,内容如下。

TLS WireShark RSA Decrypt Contents

SSLKEYLOGFILE

也就是通过 SSLKEYLOGFILE 环境变量设置文件路径,将每个 HTTPS 连接产生的 Premaster Secret 或 Master Secret 存下来,这样 Wireshark 就可以解密 HTTPS 流量,即使是使用了 ECDHE 这种具有前向安全性的密钥交换。

目前 Chrome 和 Firefox 都支持,只需要设置好环境变量即可。

另外,如果使用 OpenSSL 可以通过 -keylogfile key.log 生成 KEYLOG 文件,其内容类似如下。

SERVER_HANDSHAKE_TRAFFIC_SECRET 23f579bda4206b808ec...
EXPORTER_SECRET 23f579bda4206b...
SERVER_TRAFFIC_SECRET_0 23f579bda4206b80...
CLIENT_HANDSHAKE_TRAFFIC_SECRET 23f579bda4206b...
CLIENT_TRAFFIC_SECRET_0 23f579bda4206b808ec...

其它

常见问题

无法识别解析协议

当通过 WireShark 打开 HTTPS 流量包时,在协议部分只会显示 TCP 而非 SSL 信息,尤其是在使用非标的端口时。可以在 [Edit]->[Preference]->[Protocols]->[HTTP] 中设置,确保 SSL/TLS Ports 中含有该端口,且在 TCP ports(s) 中不含。

其它客户端支持

其中 curl 在 7.58.0 之后的版本也支持使用上述的环境变量,所以可以使用与 Firefox 和 Chrome 类似的方式。如果使用的是 OpenSSL 客户端,可以通过 -debug 参数将相关的信息打印出来,直接使用 SSL-Session 中的内容即可。

SSL Key Log 中还提供了通过 PRE_LOAD 环境变量设置的方式,同样用来 dump 密钥信息。

另外 Extract Pre-master keys from an OpenSSL application 中也介绍了通过 GDB 查看内存中的信息。