前言
最近在读 WireGuard® 的源码和论文时使用了 Wireshark 进行抓包分析,本文是对这个过程(如何通过 Wireshark 实时抓取 WireGuard 加密包并解密1)的一点记录。
内容
思路
- 在虚拟化准备两台虚拟机用以构建一个 WireGuard VPN 测试网络,其中虚拟机 A 作为请求发起方,虚拟机 B 作为响应方。
- 在本地 Windows 笔记本上安装 Wireshark(包含 sshdump 功能)通过 SSH 的形式连接虚拟机 A,实现 Wireshark 远程实时抓包。
挑战
- 前向保密性2对流量分析的挑战。WireGuard 为了保证前向保密性,每隔 120s 会更换一个临时密钥(一次性的)来加密数据包。因此,如果要获取实时的明文数据,Wireshark 需要动态的获取临时密钥,并基于临时密钥实时解密。
- 需要更换内核。WireGuard 是 Linux 内核模块,笔者的测试环境需要重新编译并替换内核才能基于 Kprobe 的方式动态获取临时密钥。
具体操作
编译并更换内核
我是在虚拟机 A 上抓包,所以需要编译和替换虚拟机 A 的内核,操作见:Arch Linux Wiki
我使用的内核是这个:
# 切到 stable 分支,我实践的时候,内核版本是 6.5,commit:fa41884c1c6d
git clone git://git.zx2c4.com/wireguard-linux
按照 Arch Linux Wiki 操作,大概有这些步骤:
- 准备内核配置
zcat /proc/config.gz > .config
- 开启 WireGuard Debug
make nconfig
- 构建内核
make -j
- 构建内核模块
make modules
- 安装内核模块
make modules_install
- 构建引导镜像
make bzImage
- 安装引导镜像
cp -v arch/x86/boot/bzImage /boot/vmlinuz-linux-wg
- 准备引导配置
cp /etc/mkinitcpio.d/linux.preset /etc/mkinitcpio.d/linux-wg.preset && vim /etc/mkinitcpio.d/linux-wg.preset
- 更新引导配置
mkinitcpio -p linux-wg && grub-mkconfig -o /boot/grub/grub.cfg
一切顺利你会得到一个这样的内核:
# uname -r
6.5.0-rc4-gfa41884c1c6d
重启服务器,在 grub 页面选择新内核启动即可。
获取并配置动态临时密钥
这块参考了 Wireshark Wiki。
在虚拟机 A 执行如下操作:
git clone git://git.zx2c4.com/wireguard-tools
cd wireguard-tools/contrib/extract-handshakes
make
截止笔者写这篇文章时:contrib/extract-handshakes/extract-handshakes.sh:46
中的 Kprobe 的函数名需要修改:
diff --git a/contrib/extract-handshakes/extract-handshakes.sh b/contrib/extract-handshakes/extract-handshakes.sh
index 688f4e4..ec8ab21 100755
--- a/contrib/extract-handshakes/extract-handshakes.sh
+++ b/contrib/extract-handshakes/extract-handshakes.sh
@@ -43,7 +43,7 @@ turn_off() {
}
trap turn_off INT TERM EXIT
-echo "p:wireguard/idxadd index_hashtable_insert ${ARGS[*]}" >> /sys/kernel/debug/tracing/kprobe_events
+echo "p:wireguard/idxadd wg_index_hashtable_insert ${ARGS[*]}" >> /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable
unpack_u64() {
修改好后,我们准备 WireGuard 网络。
在虚拟机 A (IP: 192.168.188.58) 执行:
wg genkey > ./private
ip link add dev wg0 type wireguard
ip addr add 10.0.0.4/24 dev wg0
wg set wg0 listen-port 51820
wg set wg0 private-key ./private
# 假设虚拟机 B 的 public key 是 kYsXxATA9sct/bLow/IgekJcrOWRhS6wDIBaqmBuuzM=
wg set wg0 peer 'kYsXxATA9sct/bLow/IgekJcrOWRhS6wDIBaqmBuuzM=' allowed-ips '10.0.0.0/24' endpoint '192.168.188.196:51820'
ip link set wg0 up
在虚拟机 B (IP: 192.168.188.196) 执行:
wg genkey > ./private
ip link add dev wg0 type wireguard
ip addr add 10.0.0.1/24 dev wg0
wg set wg0 listen-port 51820
wg set wg0 private-key ./private
# 假设虚拟机 A 的 public key 是 glGx5x9I/hmpv3XGx7YrAnEaq/dijVeqY9xiUocneik=
wg set wg0 peer 'glGx5x9I/hmpv3XGx7YrAnEaq/dijVeqY9xiUocneik=' allowed-ips '10.0.0.0/24' endpoint '192.168.188.58:51820'
ip link set wg0 up
生产的 WireGuard 网络,我一般还会开启 PSK(PreSharedKey),但这个功能会导致 Wireshark 无法解密 WireGuard 的数据包,FYI。
然后回到虚拟机 A,执行下面的命令捕获临时密钥,命令如下:
cd wireguard-tools/contrib/extract-handshakes
./extract-handshakes.sh > wg.keys &
ping -c 1 '10.0.0.1'
wg.keys 中的内容如下:
因为 Wireshark 是在本地,所以这里还需要通过 SSHFS 将虚拟机 A 的磁盘挂载到本地 Windows 上,这样 Wireshark 才能实时读取到 wg.keys。
在本地 Windows 上安装 SSHFS 的 PowerShell 命令如下:
winget install --id SSHFS-Win.SSHFS-Win --force
通过 SSHFS 将该虚拟机磁盘挂载到 Windows 资源管理器内:
截图中的地址如下:
\\sshfs.k\\<username>@<host>
一切顺利的话会是这个样子:
Wireshark 基于 SSH 远程实时抓包操作见:
Wireshark 配置动态临时密钥见:
视频末尾演示了如何通过 Wireshark 抓取 WireGuard 中的 HTTP 包。
抓取时可以使用下面的捕获规则,只抓取 WireGuard 的数据包:
udp[8:1] >= 1 and udp[8:1] <= 4 and udp[9:1] == 0 and udp[10:2] == 0
抓取 WireGuard 中的 HTTPS 包并解密:
总结
以上就是我通过 Wireshark 抓取 WireGuard 数据包并实时解密的全部操作。 如果你发现本文中存在任何语法错误,欢迎通过 主页 中 Email 联系我,另外你也可以在 关于 里面找到我的微信二维码。