💥 荒诞破门现场:当防 DoS 错误码撞上了账户锁定码
系统在 /etc/shadow 里把 root 密码写成 *,本意是“该账户无密码,永远不可能匹配”。黑客输入超长 257 字节垃圾密码,musl 库为了防 DoS 拒绝哈希并返回 "*"。OpenSSH 拿 "*" 与 "*" 比对,发现两边一模一样,当场判定密码正确开门!
2026 年 11 月 5 日,OpenSSH 紧急发布 10.6p2 修复了一个潜伏整整十年的惊天逻辑漏洞 CVE-2026-123158(代号 *sshole)。在 Alpine Linux、OpenWrt 等基于 musl libc 的轻量 Linux 系统上,对于原本专门设置成【禁止密码登录(shadow 里是 * 号)】的 root 或密钥部署账户,黑客只要输入任何一段长度超过 256 字符的长密码,sshd 居然当场放行开门!
一文彻底拆解:2012 年 musl 限制密码防 DoS、2016 年 OpenSSH 防用户枚举借用 Salt、2022 年 Alpine 将 root 锁为星号的三方历史碰撞,以及为什么 "*" == "*" 酿成全网大灾难。
系统在 /etc/shadow 里把 root 密码写成 *,本意是“该账户无密码,永远不可能匹配”。黑客输入超长 257 字节垃圾密码,musl 库为了防 DoS 拒绝哈希并返回 "*"。OpenSSH 拿 "*" 与 "*" 比对,发现两边一模一样,当场判定密码正确开门!
修复手段极其冷静干脆:sshd 在做密码字符串比对前,强制检查目标 shadow 里的哈希长度是否太短!合法的 sha512crypt 哈希至少有几十个字符,若发现字段只是区区一个 * 号,直接认定为非法凭证拒绝登录,彻底终结星号巧合。
在 Linux 常用 C 库中,glibc 可以吃下几千字符的密码;但专注于嵌入式轻量的 musl libc 开发者发现,密码越长 sha512crypt 运算耗时越呈指数级飙升。为了防止单个大密码卡死 CPU,musl 硬编码了 256 字节上限,超长直接返回固定标记 *。
偏偏 * 在 Unix 历史上刚好代表“被禁用的空密码”。
如果目标用户在 /etc/shadow 里没有密码哈希,原本根本不应该去调 crypt()。但 OpenSSH 为了防御针对用户名的侧信道枚举攻击(Timing Attack),刻意从系统里抓取另一个已有密码用户的 Salt 假装算一遍,无意间把超长密码喂给了 musl!
原本用于保护隐私的安全特性,反向把钥匙递到了黑客手里。
2019 年 Alpine 曾因 root 密码为空(CVE-2019-5021)用 ! 锁死账户。但运维发现没有 PAM 的环境下,! 会连带干掉公钥登录!为了让 AWS/Azure 云镜像能正常用 SSH Key 登入,Alpine 在 3.16 把 root 换成了 *。
由此,全部多米诺骨牌彻底就位。
点击下方步骤,看认证流程如何在每一步逻辑中阴差阳错完美放行:
注:本探针严格按照 sshhole.com 披露记录与 OpenSSH 10.6p2 源码修复逻辑构建。
必须同时满足以下所有条件才会被破门:
• 系统基于 musl libc(Alpine Linux、OpenWrt、Buildroot);
• 没有使用 PAM 模块(Alpine 默认未启用 PAM);
• 开启了密码登录(PasswordAuthentication yes 默认开启);
• 目标账户密码字段是 *(root 或仅公钥部署用户);
• 系统中至少有另外一个用户设置了正常密码(供 sshd 借 Salt)。
在 AWS、Azure 官方 Alpine 镜像中,默认用户 alpine 密码就是 *,而且它拥有免密码执行 doas(Alpine 的 sudo)的最高权限!黑客一旦随手敲入 257 字符登入 alpine 账户,1 秒内直接跃迁为系统最高 root!
Ubuntu、Debian、CentOS、RHEL 等绝大多数使用 glibc 的发行版安全;开启了 PasswordAuthentication no 的服务器安全;密码字段为 ! 的账户安全;仅使用 Dropbear 的小型路由器安全。
| 操作项目 | 标准执行命令 / 建议 | 通俗原理解释 |
|---|---|---|
| 第一步:升级软件包 | apk upgrade -U openssh |
安装已经合并修复断言的 OpenSSH 10.6p2 或对应 LTS 回移植补丁 |
| 第二步:必须重启服务 | rc-service sshd restart |
切记!仅升级 apk 不会替换内存中跑着的旧守护进程,必须重启生效 |
| 应急缓和:关掉密码 | echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/00-no-pwd.conf |
如果由于生产环境无法立即重启机器,强制关闭密码通道仅留密钥 |
| 排查是否已被入侵 | grep "Accepted password" /var/log/messages |
检查日志里是否有原本没有密码的账户(root、alpine)居然通过密码成功登录 |
| 重构容器基座镜像 | 重新拉取最新的 alpine:latest 构建 Docker |
防止新发布的无状态容器依然继承老旧带洞的 Base 镜像 |
“三个各自合理的设计:一个为了防死机限制长度输出星号,一个为了防窃听假装借用盐算星号,一个为了禁密码在档案里记录星号——十年前埋下的三颗无辜齿轮,在时光长河中严丝合缝地咬合,最终在推开 root 大门的一瞬间,成了全人类安全史上最荒诞的一记回形镖。”
安全往往不取决于高深莫测的数学加密算法,而往往失陷在那些最无聊、最不起眼、没人愿意深究的系统胶水缝隙之中。