2024 年 7 月,Qualys 公开了 CVE-2024-6387。漏洞在 OpenSSH 的 sshd 里,未认证的攻击者有可能以 root 身份执行代码。消息传开后,很多团队的第一反应是改 LoginGraceTime。
这个动作有用,但它不是修复。搞清楚它挡的是什么、代价是什么,比抄一条配置更重要。
背景:一个 2006 年修过的问题又回来了#
sshd 在等待客户端完成认证时,会用 SIGALRM 做超时控制。信号处理函数里只能调用异步信号安全的函数,这是 POSIX 的硬约束,因为信号可能打断任何正在执行的代码。
2006 年曾有一个同类问题(CVE-2006-5051),当时的修法是把处理逻辑挪出信号上下文。几年后代码重构,某次改动又让信号处理函数里出现了非异步信号安全的调用,问题被重新引入。这就是 “regreSSHion” 里 regression 的来源。
受影响的是 glibc 环境下的 OpenSSH,官方在 9.8p1 中修复。具体的版本区间请以 OpenSSH 与各发行版的安全公告为准,不要只凭网上流传的列表判断。
公开节奏也值得记一笔。Qualys 在 2024 年 5 月向 OpenSSH 报告,双方约定 7 月 1 日同步公开。留给发行版的时间只有几周,不少二进制包在最后几天才打上补丁。所以“看到新闻就升级”未必能立刻拿到修复包,先看包版本,再决定要不要等。
漏洞原理:信号处理里的竞态#
点到为止:认证超时触发 SIGALRM,处理函数在极窄的时间窗口里操作了共享状态。攻击者通过大量连接去命中这个窗口,就可能让 sshd 以 root 身份执行非预期代码。
三个前提值得记住。必须是 glibc 环境,用 musl 的 Alpine 不在同一影响面;需要大量连接尝试,命中窗口还有运气成分。因此它更像“可以被耐心攻击的洞”,而不是一条 payload 就进。
但它依然严重:sshd 通常直接暴露在公网,且以 root 运行。一旦命中,攻击者拿到的是整台机器。
为什么 32 位更容易?地址空间小,内存布局的随机性弱,命中竞态窗口的概率就高;64 位下同一套手法需要更多次尝试。这不改变修复方式,但能解释为什么公开分析里 32 位设备被单独点名。
影响范围#
| 环境 | 判断 | 备注 |
|---|---|---|
| glibc 发行版 + OpenSSH | 按公告确认 | 命中难度与架构相关 |
| Alpine / musl | 不受该竞态影响 | 仍应保持升级 |
| 仅密钥认证、sshd 不暴露公网 | 风险降低 | 不等于免疫 |
| 容器内自建 sshd | 常被漏掉 | 单独排查 |
| 发行版已回填补丁 | 可能已修复 | 版本号不一定变 |
最后一行是重点。发行版经常只回填补丁、不改版本号,ssh -V 显示 8.0p1 不代表一定没修。
容器与 Kubernetes 节点也要算进来。很多镜像里装了 openssh-server,只为跑 CI 的 ssh 步骤,宿主机的补丁策略覆盖不到它。节点上的 sshd 如果监听在 host 网络,风险和物理机一样。
检测:三步确认版本与补丁状态#
# 1. 看版本(客户端与服务端通常同源)
ssh -V
# 2. 看发行版的包与补丁记录
rpm -q openssh-server 2>/dev/null || dpkg -l openssh-server
rpm -q --changelog openssh-server 2>/dev/null | head -40 # RHEL 系
apt changelog openssh-server 2>/dev/null | head -40 # Debian 系
# 3. 确认是否为 glibc 环境
ldd /usr/sbin/sshd | head -5
第二条比第一条可靠。补丁回填会写进 changelog,通常带 CVE 编号。Debian 系还可以看 /usr/share/doc/openssh-server/changelog.Debian.gz。
第二条检测线是连接行为。这类攻击需要海量尝试,日志里会出现密集的 “Timeout before authentication” 或 “Connection closed by authenticating user”。配一个阈值告警,比事后翻日志有用。
# 最近一小时的认证超时次数
journalctl -u ssh --since '-1 hour' | grep -c 'Timeout before authentication'
# 超时来源 IP 排行(字段按你的日志格式调整)
grep 'Timeout before authentication' /var/log/auth.log \
| grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | sort -rn | head
另一条信号是 sshd 子进程异常退出。竞态命中失败时可能让子进程崩溃,表现为 systemd 反复拉起服务,或日志里出现 fatal 与段错误。用 journalctl -u ssh 过滤这两个关键词,出现频率异常就值得深挖。
修复与缓解:补丁优先,参数是过渡#
优先级很明确。
- 升级到发行版提供的最新
openssh-server。升级前备份sshd_config与主机密钥,先在测试机验证登录、端口转发、SFTP 是否正常,再灰度。 - 不能立刻升级时,把
LoginGraceTime设为 0。代价是未完成认证的连接不再被超时清理,攻击者可以用连接数把MaxStartups占满,形成拒绝服务。所以同时要下调MaxStartups、配合防火墙限速。 - 收窄暴露面:只允许固定来源访问 22,或走跳板、VPN;禁用密码认证。
# /etc/ssh/sshd_config.d/99-hardening.conf
LoginGraceTime 30 # 不建议长期设 0,见上文的 DoS 取舍
MaxStartups 10:30:60 # 超过阈值随机拒绝,压制连接洪水
PermitRootLogin prohibit-password
PasswordAuthentication no
改完先校验语法,再用 reload,不要 restart。reload 不会掐断已建立的会话。
如果发行版用 socket 激活,reload 的行为和传统服务不同,先用 systemctl list-units 'ssh*' 确认到底在跑哪个单元。
sudo sshd -t && sudo systemctl reload ssh
远程改 sshd 最常见的翻车方式:改错一行、重启、然后自己被锁在外面。改之前 sshd -t,改之后新开一个窗口验证能登录,确认无误再关闭旧会话。手边留一个云厂商的 VNC 或串口控制台。
验证:看生效值,不看配置文件#
sshd -T | grep -E 'logingracetime|maxstartups|permitrootlogin|passwordauthentication'输出的是实际生效值,sshd -T会把 include 的文件也算进去。- 从另一台机器执行
ssh -o PreferredAuthentications=none user@host,应立即被拒,而不是挂着等超时。 - 观察一周的连接日志:超时次数是否下降,是否有单一 IP 的高频尝试。
- 若做过主机密钥轮换,确认
known_hosts的更新流程已经通知到使用方。 - 做一次断网演练:在控制台里模拟 sshd 起不来,确认你有 VNC、串口或救援模式可用。没有这条后路,任何远程改 sshd 的操作都是在赌。
结论#
- regreSSHion 的本质是信号处理竞态,修法是升级,不是改一个参数。
LoginGraceTime 0能挡住竞态,但换来连接耗尽的风险,只适合作为过渡。- 发行版回填补丁不改版本号,判断是否修复要看包版本和 changelog。
- 检测侧最实用的信号是认证超时突增,配阈值告警比翻日志快。
- 改 sshd 前先
sshd -t,改完新窗口验证,再关旧会话。
完