OpenSSH 10.6p2 紧急修复:Alpine 上一句超长密码就能跳过 SSH 认证

OpenSSH 项目在 11 月 5 日发布 10.6p2,修掉一个能让攻击者用一句超长密码直接登录「没有密码」账户的缺陷。受影响的是 Alpine Linux 这类基于 musl C 库、且 openssh-server 未编译 PAM 的系统。攻击者只需要网络可达、一个账户名,以及一个长度在 257 到 1024 字节之间的密码——单次连接、每次必中。OpenSSH 官方给这个缺陷定级 critical,截至发稿尚未分配到 CVE 编号。

对跑 Alpine 容器、OpenWrt 设备或自建 musl 环境的运维来说,这是一条必须在今天处理完的工单。

事件概述

缺陷由 Özgün Kültekin(Wakefield)报告,技术细节发布在其个人站点 sshhole.com。OpenSSH 维护者 Damien Miller 在 11 月 5 日的发布公告中确认了影响范围,并对仍在受影响系统上的用户给出了一句非常直接的处置意见:PasswordAuthentication=no,或者「立刻关掉这些机器的 SSH」。

Alpine 同日发布安全公告,并按分支推送了修复包:3.21.10、3.22.8、3.23.8、3.24.4 等点版本已包含修复,3.16 至 3.21 虽已过支持期,也一并给了修复包。截至发稿没有发现利用该缺陷的攻击。

技术细节

问题不在 sshd 的认证逻辑本身,而在 musl 的 crypt() 失败返回值与 sshd 的比较方式撞到了一起。

不走 PAM 时,sshd 自己校验密码:把尝试的密码交给 crypt() 做哈希,再与 /etc/shadow 里的存储字段比对。没有设置密码的账户,该字段只有一个 *。这个长度连盐都放不下,于是 sshd 会去「借」另一个有真实哈希账户的盐——这段逻辑来自 2016 年 7 月 15 日为修 CVE-2016-6210(用户名枚举)而做的改动。

关键是 musl 在 2012 年 8 月 9 日的一处变更:crypt() 失败时不再返回 NULL,而是返回「一个无法匹配的哈希」,因为大量程序不检查失败返回值。随后 8 月 29 日为防 DoS 又加了 256 字节上限。两条叠加的结果是:对 $5$、$6$ 两种哈希,密码超过 256 字节时 crypt() 直接返回 *——而账户存储字段恰好也是 *,比较通过,登录成功。上限 1024 字节则是 sshd 自身对密码长度的限制。

完整触发需要四个条件同时成立:

  • 运行 openssh-server(未编译 PAM),或 openssh-server-pam 但配置了 UsePAM no
  • PasswordAuthentication yes,这是 OpenSSH 默认值
  • 目标账户的密码字段为 *,即未设密码
  • 机器上至少还有一个账户的密码哈希以 $5$ 或 $6$ 开头

在 Alpine 上,无密码账户并不罕见:用 adduser 不设密码创建、或用 usermod -p '*' 处理的仅密钥用户;通过 musl-nscd 接入的目录服务用户(没有 shadow 条目);root(自 Alpine 3.16 起基础镜像里就是 root:*);以及官方 tiny-cloud 云镜像里的 alpine 用户。root 默认受 PermitRootLogin prohibit-password 保护,但只要暴露的账户能借 doas 提权,这层保护就形同虚设——3.18.11 至 3.24.3 的 tiny-cloud 镜像给 alpine 用户配置了免密 doas,首次启动时安全,一旦机器上出现任意一个 $6$ 哈希(比如管理员设了控制台密码),通道立刻打开。

影响评估

危害等级不看攻击复杂度,看暴露面。该缺陷不需要爆破、不需要字典、不需要多次尝试,一次请求就出结果,因此任何扫描器只要知道账户名,就能秒级确认并利用。风险评估时按「未认证远程登录」对待,而不是按「弱口令」对待。

不受影响的情形需要明确:非 Linux 系统、基于 glibc 的 Linux、走 PAM 校验的系统、不跑 sshd 的容器、PasswordAuthentication no 的机器、用 cloud-init 写入该配置的云镜像、以及 shadow 里用 ! 锁定的账户。Kültekin 另外点名 OpenWrt、Buildroot、Yocto 三个 musl 系统需要自查,不过 OpenWrt 默认用 Dropbear 处理 SSH,Dropbear 不受影响——只有当管理员在 OpenWrt 上另装了 OpenSSH 服务端才需要考虑。

修复建议

升级包不会重启运行中的 sshd,两步都要做:

apk upgrade -U -a
rc-service sshd restart

自己编译 OpenSSH 的,10.6p2 已包含改动。10.6p2 的做法是:当存储字段短于 4 个字符时直接拒绝匹配。OpenSSH 把这个改定性为 workaround,认为根因在 sshd 之外。

暂时无法升级的,先确认密钥登录可用,并保持当前会话不要断开,再关闭密码认证:

printf 'PasswordAuthentication no\n' > /etc/ssh/sshd_config.d/99-no-password.conf
sshd -t
rc-service sshd restart
sshd -T | grep -i passwordauthentication

排查是否已被登录:在 /var/log/messages 里搜 sshd 的 Accepted password for 记录,重点看 alpine、root 以及任何仅密钥账户;同时检查 doas 使用记录、近期新建用户、~/.ssh/authorized_keys 与近期被改动的文件。Alpine 公告的原话是:如果不能排除有人登录过,就按已失陷处理。

还有一层背景值得记下来。Miller 在发布公告里说,团队这次刻意把披露窗口压到最短,因为长封禁期对防御方几乎没有好处、对攻击者反而有利——他们最近多次看到 AI 智能体把同样的漏洞重新找出来,有时只隔几个小时。这个判断,比漏洞本身更值得国内做应急响应的同行参考。

参考来源