SSH协议全流程深度解析:从密钥交换到Wireshark实战抓包

📅 2026/8/22 23:15:01
SSH协议全流程深度解析:从密钥交换到Wireshark实战抓包
1. SSH协议从密码到密钥的信任建立之旅当你需要远程管理一台服务器或者安全地传输文件时SSHSecure Shell几乎是唯一的选择。它就像一个加密的隧道把你在本地敲击的每一个字符、传输的每一个字节都严严实实地包裹起来防止任何窥探。但你是否想过这个看似简单的“连接”动作背后究竟发生了多少轮复杂的“握手”和“协商”为什么第一次连接时会弹出一个警告公钥和私钥又是如何协同工作让你实现免密登录的理解SSH的完整流程绝不仅仅是满足技术好奇心。当连接超时、认证失败、或者速度异常时清晰的流程认知能让你像侦探一样快速定位问题究竟出在“握手”、“密钥交换”、“认证”还是“会话建立”的哪一个环节。而Wireshark这个网络世界的“显微镜”则能将协议层抽象的交互还原成一个个看得见、摸得着的网络数据包让理论照进现实。今天我们就抛开枯燥的RFC文档以一次完整的SSH连接为线索亲手用Wireshark抓取并剖析每一个数据包。我会带你走一遍从TCP三次握手开始到最终打开一个远程Shell的完整旅程。你会看到加密是如何层层加码的认证是如何步步为营的以及那些常见的连接错误在数据包层面究竟长什么样。无论你是运维工程师、开发人员还是网络安全爱好者这次深入的抓包分析都将让你对SSH的理解提升一个维度。2. 实验环境搭建与Wireshark抓包准备在开始解剖SSH协议之前我们得先准备好手术台和显微镜。一个可控的实验环境是成功抓包分析的前提它能避免公网复杂环境的干扰让我们专注于协议本身。2.1 构建本地SSH实验环境最干净、最理想的实验环境是在本地用虚拟机搭建。我推荐使用VirtualBox或VMware创建两台Linux虚拟机如Ubuntu Server一台作为客户端一台作为服务器。将它们的网络模式设置为“仅主机Host-Only”网络这样所有流量都只在你的物理主机内部循环不会被路由到外部网络也方便Wireshark在物理主机上抓取到所有流量。在服务器虚拟机上我们需要确保SSH服务正在运行。通常OpenSSH server默认可能没有安装。你可以通过以下命令来安装和启动它sudo apt update sudo apt install openssh-server -y sudo systemctl enable ssh sudo systemctl start ssh安装完成后使用sudo systemctl status ssh命令检查服务状态确认其处于“active (running)”状态。默认情况下SSH服务监听在22号端口。在客户端虚拟机上只需要安装SSH客户端即可通常它已经包含在openssh-client包中。你可以通过ssh -V命令来检查客户端版本。2.2 Wireshark配置与抓包过滤器技巧接下来是关键的抓包工具——Wireshark。请务必从官网下载安装以保证功能的完整性。安装后启动Wireshark你会看到一系列网络接口列表。在我们的“仅主机网络”环境下你需要选择对应VirtualBox或VMware创建的虚拟网卡名称可能类似“VirtualBox Host-Only Ethernet Adapter”。直接开始抓包会捕获到海量的无关数据包比如ARP广播、DHCP请求等。为了精准捕获SSH流量我们必须使用捕获过滤器。在开始抓包前在捕获过滤器的输入框中填入tcp port 22。这个过滤器告诉Wireshark“只抓取源端口或目标端口是22的TCP数据包。”因为SSH默认使用22端口这样能极大减少噪音。注意这里用的是“捕获过滤器”它在抓包时生效直接丢弃不匹配的数据包可以节省系统资源。而“显示过滤器”是在抓包后用来筛选查看的两者语法相似但作用阶段不同不要混淆。点击开始抓包后窗口可能暂时一片空白。这时从你的客户端虚拟机执行一个简单的连接测试命令ssh 服务器IP地址。如果这是首次连接会提示你确认服务器指纹输入yes后会提示你输入密码。我们先输入错误的密码让认证失败然后关闭连接。这个简单的操作会触发SSH协议从连接到失败退出的完整流程非常适合我们分析。2.3 首次连接的关键数据包保存在客户端完成一次失败的连接尝试后回到Wireshark点击停止抓包。你应该能看到一系列数据包。立即将抓包结果保存为一个文件例如ssh_analysis.pcapng。这是一个好习惯因为Wireshark的默认内存缓冲区可能有限保存文件可以确保我们后续能从容地、反复地分析这些数据。现在我们有了“手术样本”。在开始分析前我建议在Wireshark的显示过滤器栏输入ssh这样会只显示SSH协议的数据包界面会更加清晰。准备工作就绪让我们正式进入SSH协议的核心流程。3. 逐层拆解一次SSH连接的完整报文对话现在我们面对Wireshark窗口中按时间顺序排列的数据包就像拿到了一部电影的原始胶片。我们的任务是把它们按场景剪辑理解每一段对话的意义。一个完整的SSH连接大致可以分为四个阶段TCP连接建立、SSH协议版本协商、密钥交换与算法协商、用户认证。让我们跟随数据包的脚步一步步拆解。3.1 基石TCP三次握手与连接建立任何基于TCP的应用层协议都始于一次经典的三次握手。SSH也不例外。在你的抓包文件中找到最开始的三个数据包它们通常标记为[SYN],[SYN, ACK],[ACK]。数据包1客户端 - 服务器客户端发送一个TCP报文其标志位SYNSynchronize Sequence Numbers被置为1序列号Seq为一个随机数比如0。这好比客户端对服务器说“嗨我想和你建立连接我的初始序列号是X。”数据包2服务器 - 客户端服务器回应一个报文标志位SYN和ACK同时置1。它确认ACK了客户端的序列号Ack 客户端的Seq1并发出自己的初始序列号Seq为另一个随机数。这相当于服务器回答“收到你的请求了ACK我同意连接我的初始序列号是Y。”数据包3客户端 - 服务器客户端再发送一个ACK报文确认服务器的序列号Ack 服务器的Seq1。至此双向通信通道建立完成。客户端说“好的收到你的同意了我们可以开始通话了。”这个阶段在Wireshark的Info列会显示为“TCP 3-Way Handshake”。如果这一步失败可能是网络不通、防火墙拦截了22端口或者服务器SSH服务未启动。在分析SSH问题时首先确认TCP握手是否成功是排除网络层故障的第一步。3.2 握手伊始SSH协议版本协商TCP连接建立后应用层的SSH对话正式开始。紧接着三次握手你应该会看到客户端发出第一个实际携带SSH协议内容的数据包。数据包4客户端 - 服务器客户端向服务器发送一个明文报文。在Wireshark中展开这个数据包的“Secure Shell Layer”你会看到类似这样的内容SSH Version: SSH-2.0-OpenSSH_8.9p1这是一个明文字符串宣告客户端支持的SSH协议最高版本是2.0并且客户端软件是OpenSSH 8.9p1。SSH协议版本协商非常简单粗暴双方都发出自己支持的版本号如果兼容通常都支持2.0则使用两者中较低的版本实际上现在基本都固定用2.0。早期的SSH-1.0协议存在设计缺陷现已基本废弃。数据包5服务器 - 客户端服务器回应自己的版本信息例如SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6。这表示服务器也支持SSH-2.0。这个交换是明文的没有加密。因此在Wireshark里我们可以直接看到。版本协商成功后双方才会进入下一个更复杂的阶段。3.3 核心机密密钥交换与算法协商这是SSH协议最精妙、最核心的部分目的是在一个不安全的网络环境中安全地协商出一个后续用于加密通信的“会话密钥”。这个过程利用了Diffie-Hellman密钥交换算法。算法列表交换在版本协商后客户端和服务器会互相发送一个“密钥交换初始化”报文SSH_MSG_KEXINIT。在Wireshark中这些报文内容看起来是一长串用逗号分隔的算法名称。它们各自列出了自己支持的密钥交换算法如curve25519-sha256,ecdh-sha2-nistp256,diffie-hellman-group14-sha1等。用于生成共享秘密。服务器主机密钥算法如ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-256等。用于服务器身份认证。加密算法如chacha20-poly1305openssh.com,aes128-gcm,aes256-cbc等。用于加密后续传输的数据。消息认证码算法如umac-64-etmopenssh.com,hmac-sha2-256等。用于验证数据完整性。压缩算法通常是none表示不压缩。双方会从对方的列表中选择自己列表中也存在的、且优先级最高的算法。例如客户端列表是A, B, C服务器列表是C, B, D那么双方最终会选择B因为A不在服务器列表C的优先级在客户端可能低于B。Diffie-Hellman密钥交换算法选定后假设是diffie-hellman-group14-sha1双方开始执行DH交换。服务器会发送一个包含大素数p、生成元g、以及服务器公钥e的报文。客户端收到后生成自己的公钥f发送给服务器。 这个过程的精妙之处在于双方利用对方的公钥和自己的私钥可以独立计算出一个相同的“共享秘密”Shared Secret。而窃听者即使截获了网络上传输的p,g,e,f在有限时间内也无法计算出这个共享秘密基于离散对数难题。生成会话密钥与服务器认证利用这个“共享秘密”以及交换过程中所有报文的哈希值双方可以派生出一组对称密钥包括初始加密密钥IV、数据加密密钥、完整性验证密钥等。同时服务器会用自己的主机私钥对本次交换过程中所有关键数据进行签名并将签名和它的主机公钥一起发送给客户端。这是关键一步客户端此时已经有了服务器的公钥可能是第一次见。它会用这个公钥去验证签名。如果验证通过就证明了两个事实第一与我通信的对方确实拥有与这个公钥配对的私钥第二刚才的密钥交换过程没有被篡改。至此客户端完成了对服务器身份的验证。这也是为什么第一次连接时客户端会提示你“无法确认主机真实性”并显示一个公钥指纹通常是公钥的MD5或SHA256哈希值让你人工核对。你确认了这个公钥就被保存在客户端的~/.ssh/known_hosts文件里下次连接就不会再警告了。这个阶段结束后后续所有的通信都将使用刚刚协商出的对称密钥进行加密。你在Wireshark里会看到之后的SSH协议数据包的“Encrypted packet”部分变成了乱码无法直接解读。3.4 最后关卡用户认证阶段服务器身份验证通过后接下来就是客户端向服务器证明“我是谁”。SSH支持多种认证方式最常见的是密码认证和公钥认证。密码认证流程客户端发送一个认证请求SSH_MSG_USERAUTH_REQUEST声明想使用password认证方式并带上用户名。服务器回复一个认证挑战可能直接接受或要求进行PAM交互等。在我们的简单场景中服务器通常会直接进入下一步。客户端再次发送请求这次包含了加密后的密码。注意密码是在已经加密的隧道中传输的所以Wireshark看到的是加密后的数据非常安全。服务器验证密码。如果正确回复认证成功SSH_MSG_USERAUTH_SUCCESS如果错误回复认证失败SSH_MSG_USERAUTH_FAILURE并可能告知还允许哪些认证方式。公钥认证流程免密登录客户端发送认证请求声明使用publickey方式并附带用于认证的公钥。服务器检查该公钥是否存在于相应用户的~/.ssh/authorized_keys文件中。如果存在服务器生成一个随机挑战challenge。服务器将这个挑战用客户端提供的公钥加密后发送给客户端。客户端用自己的私钥解密这个挑战然后对挑战进行某种运算如签名将结果发回服务器。服务器用存储的公钥验证这个签名。验证通过则认证成功。公钥认证的安全性更高因为它不需要在网络上传送密码即使是加密的并且可以抵抗暴力破解。在Wireshark中你只能看到认证方式的声明和加密后的挑战/响应数据流无法看到私钥或密码的任何信息。4. 从理论到实战Wireshark深度排查常见SSH问题掌握了正常流程Wireshark就从一个观察工具变成了强大的排错利器。很多棘手的SSH连接问题在数据包层面都会留下清晰的蛛丝马迹。我们来看几个典型场景。4.1 案例诊断连接超时与握手失败症状客户端执行ssh userhost后长时间挂起最终报错“Connection timed out”。排查思路检查TCP握手在Wireshark中过滤tcp.port 22。观察是否有客户端发出的[SYN]包。无[SYN]包可能是客户端防火墙阻止了出站连接或者DNS解析失败你用了主机名而非IP。检查客户端防火墙规则和/etc/hosts文件。有[SYN]包但无回应服务器没有返回[SYN, ACK]。这强烈指向网络路由问题或服务器端防火墙如iptables, firewalld丢弃了22端口的入站请求。你可以在服务器上使用sudo iptables -L -n或sudo firewall-cmd --list-all来检查规则。有完整的TCP三次握手那么问题出在TCP之上。继续看后续是否有SSH版本协商包。检查SSH版本协商如果TCP握手成功但紧接着没有SSH版本字符串的交换连接就断了。可能的原因包括服务器SSH服务未运行在服务器上执行sudo systemctl status sshd确认。服务器监听地址SSH服务可能只绑定在127.0.0.1本地回环而不是0.0.0.0所有接口。检查/etc/ssh/sshd_config中的ListenAddress配置。中间设备干扰有些网络设备如某些防火墙、入侵检测系统可能会异常断开空闲的TCP连接或者错误地处理了SSH协议的初始报文。4.2 案例诊断认证反复失败与算法不匹配症状连接能建立但总是在输入密码或使用密钥后认证失败日志提示“Permission denied”或“Authentication failed”。排查思路观察认证阶段报文在Wireshark中跟随TCP流右键数据包 - Follow - TCP Stream可以更清晰地看到文本交互对于版本协商等明文部分。关注服务器返回的SSH_MSG_USERAUTH_FAILURE报文。它里面会包含一个partial success标志和auth that can continue字段。如果auth that can continue字段包含publickey说明服务器期望公钥认证但你可能未提供或提供了错误的密钥。检查客户端的-i参数或~/.ssh/id_rsa等密钥文件。如果包含password说明密码认证可用但你的密码错了。注意服务器可能配置了禁止密码登录PasswordAuthentication no此时这个字段就不会有password。深挖算法协商问题这是一个更隐蔽的坑。有时客户端和服务器支持的算法列表没有交集导致密钥交换失败。虽然连接会早期断开但错误信息可能很模糊。在Wireshark中对比仔细查看客户端和服务器发出的SSH_MSG_KEXINIT数据包。展开列表对比双方的“密钥交换算法”、“加密算法”等。例如旧的客户端可能只支持diffie-hellman-group1-sha1而现代的服务器出于安全考虑已禁用此算法只支持group14或curve25519。这就导致了协商失败。解决方案升级客户端/服务器的OpenSSH版本或者在配置文件中显式指定双方都支持的算法。例如在客户端的~/.ssh/config或服务器的/etc/ssh/sshd_config中可以使用KexAlgorithms,Ciphers,MACs等指令进行配置。4.3 高级技巧解密SSH加密流量与跟踪应用数据默认情况下Wireshark无法解密SSH流量因为会话密钥是在内存中动态生成的。但是对于调试和深度安全分析OpenSSH提供了一种将密钥材料导出供Wireshark使用的机制。步骤设置环境变量在启动SSH客户端时设置一个特殊的环境变量指示OpenSSH将会话密钥写入一个文件。SSH_DEBUG1 SSH_AUTH_SOCK ssh -o SetEnv SSH_DEBUG1 -o SetEnv SSH_DEBUG_WIRESHARK1 userhost更通用的方法是在客户端机器的~/.ssh/config文件中为特定主机配置Host debug_host HostName your_server_ip User your_username SetEnv SSH_DEBUG_WIRESHARK1然后使用ssh debug_host连接。连接成功后在当前目录会生成一个名为ssh-XXXXXX.log的文件XXXXXX是随机字符。在Wireshark中加载密钥打开Wireshark进入编辑 - 首选项 - 协议 - SSH。在“RSA keys list”或“Decryption keys”区域点击“浏览”选择刚才生成的.log文件。Wireshark会自动识别其中的密钥。重新加载抓包文件加载密钥后关闭再重新打开你的ssh_analysis.pcapng文件或者如果你正在实时抓包后续的SSH数据包就会被自动解密。此时原本显示为“Encrypted packet”的数据现在可以展开看到内部的SSH_MSG_CHANNEL_DATA等内容甚至能看到你输入的每一个命令和服务器返回的每一个字符。重要警告此方法导出的密钥文件是高度敏感的它允许任何人解密此次会话的所有通信。务必仅在绝对安全的调试环境中使用并在调试结束后立即彻底删除该密钥文件。通过这个技巧你就能真正“看到”加密隧道内的所有活动对于理解SSH通道、端口转发、SFTP等高级功能的数据流非常有帮助。当然这也从另一个角度证明了SSH协议的安全性——在不知道这个特定会话的密钥文件的情况下即使截获了所有流量也无法解密其内容。