musl 上的 OpenSSH 密码绕过,10.6p2 已修复 (CVE-2026-123158)
musl 上未启用 PAM 的 OpenSSH 会接受锁定账户的 257 字节密码;已在 10.6p2 和 Alpine 软件包中修复。
TL;DR
- 使用 musl libc 且未启用 PAM 编译的 OpenSSH sshd 会接受 shadow 密码字段为
*的账户的任何超过 256 字节的密码。OpenSSH 项目于 2026 年 11 月 5 日发布 CVE-2026-123158 和 10.6p2。 - 在启用密码认证的情况下运行 sshd 的 Alpine Linux 服务器,如果另一个本地账户有
$5$或$6$哈希,就会受到影响。Alpine 官方镜像被广泛使用;不运行 sshd 的容器不受影响。 - MITRE 评分为 CVSS v3.1 8.1 High (CWE-394)。存在一个公开实验室和两个概念验证仓库;未报告在野利用,截至 2026 年 11 月 6 日该 CVE 不在 CISA KEV 目录中。
- 升级到 OpenSSH 10.6p2,或升级到所在分支的 Alpine 修复包,然后重启 sshd。如果无法升级,请设置
PasswordAuthentication no;参见“如何修复?”。
我受影响吗?
只有同时满足以下所有条件时才会受到影响:
- 主机运行 10.6p2 之前的 OpenSSH,或低于表中修复版本的 Alpine openssh 软件包。
- sshd 使用 musl 编译并在未启用 PAM 的情况下运行。Alpine 的
openssh-server未启用 PAM 编译。openssh-server-pam配合UsePAM yes不受影响;同一软件包配合UsePAM no则受影响。 PasswordAuthentication yes生效。这是 OpenSSH 的默认值。- 至少一个可通过密码访问的账户的 shadow 密码字段为
*。在 Alpine 上,这包括 root(自大约 Alpine 3.16 起基础镜像中为root:*)、使用adduser创建且没有密码的仅密钥用户、没有 shadow 条目的目录服务用户,以及官方 tiny-cloud 镜像中的alpine用户。 - 至少一个本地账户有
$5$或$6$crypt 哈希,以便 sshd 借用盐。完全没有 SHA-256 或 SHA-512 哈希的机器不会触发此缺陷。
对于 root,默认的 PermitRootLogin prohibit-password 仍会阻止密码登录。当设置 PermitRootLogin yes,或另一个可登录的 * 账户有通往 root 的路径(例如 tiny-cloud 镜像上无密码的 doas)时,root 才可被访问。
不运行 sshd 的容器不受影响。用 ! 锁定的账户无法通过密码或密钥登录,因此不是目标。设置 PasswordAuthentication no 的系统、使用 openssh-server-pam 且 UsePAM yes 的系统,以及没有任何 $ 形式哈希的系统也不在此缺陷范围内。
| 分支 | 受影响版本 | 修复版本 |
|---|---|---|
| OpenSSH 上游(musl,禁用 PAM) | 10.6p1 及更早 | 10.6p2(2026 年 11 月 5 日发布) |
| Alpine edge | 低于 10.6_p2-r0 | 10.6_p2-r0 |
| Alpine 3.24 | 低于 10.3_p1-r2 | 10.3_p1-r2(点版本 3.24.4) |
| Alpine 3.23 | 低于 10.2_p1-r1 | 10.2_p1-r1(点版本 3.23.8) |
| Alpine 3.22 | 低于 10.0_p1-r11 | 10.0_p1-r11(点版本 3.22.8) |
| Alpine 3.21 | 低于 9.9_p2-r1 | 9.9_p2-r1(点版本 3.21.10) |
| Alpine 3.20 | 低于 9.7_p1-r6 | 9.7_p1-r6 |
| Alpine 3.19 | 低于 9.6_p1-r3 | 9.6_p1-r3 |
| Alpine 3.18 | 低于 9.3_p2-r4 | 9.3_p2-r4 |
| Alpine 3.17 | 低于 9.1_p1-r7 | 9.1_p1-r7 |
| Alpine 3.16 | 低于 9.0_p1-r6 | 9.0_p1-r6 |
Alpine 为 3.16 到 3.24 的每个分支以及 edge 发布了修复包,包括不再接收定期更新的版本。当前包含修复的四个点版本是 3.21.10、3.22.8、3.23.8 和 3.24.4。
检查主机运行的内容:
# Alpine: the installed OpenSSH packages and versions
apk info -v openssh-server openssh-server-pam 2>/dev/null
# Whether this sshd uses PAM (no means the vulnerable build on Alpine)
sshd -T 2>/dev/null | grep -i '^usepam'
# Whether password authentication is on
sshd -T 2>/dev/null | grep -i '^passwordauthentication'
# Accounts whose shadow password field is exactly *
sudo awk -F: '$2=="*"{print $1}' /etc/shadow
# Whether any local account has a SHA-256 or SHA-512 hash
sudo awk -F: '$2 ~ /^\$[56]\$/{print $1}' /etc/shadow
sshd -T 需要 root 和可读的配置。在非 Alpine 的 musl 系统(如 OpenWrt、Buildroot 或 Yocto)上,请使用发行版的包管理器并运行 ssh -V 来确定 OpenSSH 构建版本,然后与供应商的修复包比较。
如何修复?
- 在 Alpine 上升级软件包并重启 sshd:
apk upgrade -U -a rc-service sshd restart升级不会重启正在运行的 sshd,因此必须重启。从修复后的基础镜像或软件包重建任何容器镜像,并重新创建容器;只拉取新镜像而不重建容器会让旧进程继续运行。 - 在其他 musl 系统上,将 OpenSSH 升级到 10.6p2 或发行版的修复包。OpenSSH 10.6p2 发行说明说明了修复内容,Alpine 公告列出了每个分支的修复包。
- 如果无法升级,请关闭密码认证。保持当前会话打开,并在关闭前测试密钥登录。在 Alpine 3.19 及更高版本上,drop-in 文件即可:
mkdir -p /etc/ssh/sshd_config.d echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00-no-passwords.conf sshd -t && rc-service sshd restart sshd -T | grep -i passwordauthentication在忽略 drop-in 目录的旧版 Alpine 上,直接编辑/etc/ssh/sshd_config并重启 sshd。设置PasswordAuthentication no也会移除有真实哈希账户的密码登录;这是预期的代价。 - 如果需要密码认证且无法升级,请删除
*目标或使其不可达。将未使用账户的*字段改为!来锁定账户,或删除这些账户。!也会阻止密钥登录,请保持另一个管理会话开启。这会关闭特定账户路径,但不会修复比较逻辑,因此请尽快升级。
根本原因是什么?
该缺陷是比较两个都为 * 的字符串:一个是因为账户没有密码,另一个是因为 musl 的 crypt() 将 * 用作错误值。在 auth-passwd.c 中,未启用 PAM 的 sshd 取出存储的密码字段,并用它哈希提供的密码:
char *pw_password = authctxt->valid ? shadow_pw(pw) : pw->pw_passwd;
if (pw_password == NULL)
return 0;
/* Check for users with no password. */
if (strcmp(pw_password, "") == 0 && strcmp(password, "") == 0)
return (1);
/*
* Encrypt the candidate password using the proper salt, or pass a
* NULL and let xcrypt pick one.
*/
if (authctxt->valid && pw_password[0] && pw_password[1])
salt = pw_password;
encrypted_password = xcrypt(password, salt);
/*
* Authentication is accepted if the encrypted passwords
* are identical.
*/
return encrypted_password != NULL &&
strcmp(encrypted_password, pw_password) == 0;
对于字段为 * 的账户,pw_password[1] 是结尾的 NUL,因此 salt 保持为 NULL。xcrypt() 随后调用 pick_salt(),遍历账户数据库直到找到 $5$ 或 $6$ 字段,并返回该哈希的类型和盐:
if (salt == NULL)
salt = pick_salt();
...
crypted = crypt(password, salt);
musl 的 SHA-512 实现拒绝超过 256 字节的密钥。该限制是拒绝服务防护,因为 SHA-512 crypt 会随密钥增长而变慢:
#define KEY_MAX 256
...
/* reject large keys */
for (i = 0; i <= KEY_MAX && key[i]; i++);
if (i > KEY_MAX)
return 0;
当内部函数返回 0 时,musl 的 __crypt_sha512() 返回字符串 *:
p = sha512crypt(key, setting, output);
...
if (!p || q != testbuf || memcmp(testbuf, testhash, sizeof testhash))
return "*";
return p;
sshd 将返回的 * 与存储的 * 比较并接受登录。OpenSSH 将提供的密码限制为 1024 字节,因此成功的范围是 257 到 1024 字节。CVE 记录描述为 256 到 1024;256 字节的密钥仍会被 musl 接受并生成真实哈希,因此 257 是触发错误返回值的最短长度。
该缺陷在 2016 年变得可达,当时 OpenSSH 添加了 pick_salt() 作为 CVE-2016-6210 的时序侧信道修复。在此之前,* 账户使用固定的 DES 盐 xx 进行哈希,不会返回 *。
如何被利用?
- 攻击者找到一台启用密码认证的 musl 系统上的 SSH 服务。
- 目标有一个可通过密码登录的
*账户:设置了PermitRootLogin yes的 root、仅密钥的部署用户、目录用户,或 tiny-cloud 镜像中的alpine用户。 - 另一个本地账户有
$5$或$6$哈希,因此 sshd 可以借用其盐。 - 攻击者向
*账户发送 257 到 1024 字节的密码。字符内容无关紧要。 - sshd 使用借来的盐哈希长密码。musl 返回
*;与存储的*比较成功,攻击者获得该账户的 shell。
sshhole.com 的公开实验室允许在浏览器中尝试该比较,CVE 记录列出了两个概念验证仓库。截至 2026 年 11 月 6 日,没有公开报告的在野利用。
攻击如何展开?
针对无密码账户的长密码到达 musl 的 * 错误返回值,而存储字段也是 *,因此 sshd 接受了它。
攻击者能做什么?
攻击者可以登录任何密码认证可到达的 * 账户,并拥有该账户的权限。影响取决于账户:
- 设置了
PermitRootLogin yes的 Alpine root:完全控制主机。 - tiny-cloud 镜像中的
alpine用户,拥有无密码doas:实例 root。 - 仅密钥的部署用户:攻击者获得该用户本应拥有的 shell,包括其密钥、云凭据和应用访问权限。
- 没有 shadow 条目的目录服务用户:攻击者冒充该用户。
“我受影响吗?”中的所有条件都适用:musl、禁用 PAM、启用密码认证、* 账户、另一账户有 $5$ 或 $6$ 哈希,以及网络可达性。该缺陷不会泄露或破解真实密码哈希,也不影响用 ! 锁定的账户。它不是绕过 SSH 协议意义上的预认证缺陷;攻击者仍然完成正常的密码登录,只是使用了服务器本应拒绝的密码。
为什么修复有效?
修复为存储的哈希增加了最小长度,并拒绝比较单字符字段:
+#define MIN_PASSWORD_HASH_LEN 4
#define MAX_PASSWORD_LEN 1024
@@
- return encrypted_password != NULL &&
+ ret = encrypted_password != NULL &&
+ strlen(pw_password) >= MIN_PASSWORD_HASH_LEN &&
strcmp(encrypted_password, pw_password) == 0;
+ freezero(pw_password, strlen(pw_password));
+ return ret;
* 字段只有一个字节,低于新的四字节最小值,因此即使 musl 返回 *,比较也为假。真实的 SHA-512 字段远长于四个字节:$6$、盐和哈希。同样的更改应用于 portable 和 UnixWare 密码路径,修复还会释放 shadow 副本。OpenSSH 项目称其为规避措施,因为根本原因在 musl 的错误值,但它关闭了 sshd 路径。Alpine 的稳定版软件包带有相同补丁。
如何检查是否已遭入侵?
没有公开的入侵指标。成功登录会为一个本不应接受密码的账户留下 Accepted password 行,因此从这里开始:
# Alpine and other syslog-based systems
sudo grep -i "Accepted password" /var/log/messages
# Compare the account name with the * accounts on the host
sudo awk -F: '$2=="*"{print $1}' /etc/shadow
命中是线索,不是证据:有真实哈希的账户也会产生 Accepted password 行。请将源地址和时间与你自己的登录记录对照。然后检查 doas 和 sudo 的使用、最近创建的用户、新的 authorized_keys 条目,以及该账户能访问的文件或服务。在 tiny-cloud 镜像上,检查 alpine 用户是否从你不认识的地址登录。
失败的尝试不会留下特殊痕迹,成功的尝试可能已从日志中删除。如果无法排除登录,请将主机视为已遭入侵,从可信镜像重建,并轮换其能访问的所有凭据。
时间线
- 2012-08:musl 更改
crypt(),在无法计算哈希时返回*而不是NULL。 - 2012-08-29:musl 将 SHA-512 crypt 密钥限制为 256 字节,使长密钥路径返回
*。 - 2016-07:OpenSSH 为无效用户添加
pick_salt(),作为 CVE-2016-6210 的时序修复,使*比较变得可达。 - 2026-10-30:OpenSSH 安全审查修复
pick_salt();有缺陷的比较就在其下方四行。 - 2026-11-05:OpenSSH 10.6p2 发布,CVE-2026-123158 公布,Alpine 发布公告和修复包。
- 2026-11-06:没有公开报告的在野利用;该 CVE 不在 CISA KEV 目录中。
常见问题
这会影响 Debian、Ubuntu 或 Fedora 吗?
不会。这些系统使用 glibc 和 PAM,而绕过需要禁用 PAM 的 musl * 错误返回值。相同版本的 OpenSSH 在 glibc 上不会走这条路径。
我使用 Alpine,但只用密钥登录。我受影响吗?
是的,如果 PasswordAuthentication 仍开启且存在 * 账户。攻击者用长密码登录 * 账户;你自己的密钥登录方式无关紧要。请设置 PasswordAuthentication no 或升级。
root 默认可以被利用吗?
默认不能。Alpine 的 root 字段为 *,但 OpenSSH 默认的 PermitRootLogin prohibit-password 阻止 root 的密码登录。当设置 PermitRootLogin yes,或另一个 * 账户可以提权(例如带无密码 doas 的 alpine 用户)时,root 才可被访问。
有可跟踪的 CVE 吗?
有。CVE-2026-123158 于 2026 年 11 月 5 日发布,CVSS v3.1 8.1 High,CWE-394。OpenSSH 公告和 Alpine 公告是主要的修复来源。
它被在野利用了吗?
截至 2026 年 11 月 6 日没有公开的利用报告,CISA 的 SSVC 评估记录了公开概念验证,但没有记录活跃利用。该缺陷足够古老,仅凭公开数据无法排除一次安静的登录,因此请检查日志。
我需要轮换 SSH 密钥吗?
只有当你发现无法解释的登录证据时才需要。如果主机被访问过,请将其能访问的所有密钥和凭据视为已泄露,并在重建后轮换。如果日志干净,升级并重启 sshd 即可。
使用 Ostorlab 发现暴露的 SSH 服务
Ostorlab 的攻击面发现会清点你暴露在互联网上的主机及其服务。用它列出在 TCP 22 端口响应主机的机器,然后优先检查它们的 OpenSSH 构建和密码认证设置。
参考文献
- OpenSSH 10.6p2 发行说明,2026 年 11 月 5 日,密码认证绕过及对 Özgün Kültekin 的致谢:https://www.openssh.org/releasenotes.html#10.6p2 (2026 年 11 月 6 日核查)
- OpenSSH 提交 4353fdc43ef646c43a8dda7025cc1a898caf1482,“avoid password authn bypass”,2026 年 11 月 5 日,
MIN_PASSWORD_HASH_LEN检查:https://github.com/openssh/openssh-portable/commit/4353fdc43ef646c43a8dda7025cc1a898caf1482 (2026 年 11 月 6 日核查) - CVE-2026-123158 记录,MITRE,2026 年 11 月 5 日发布,CVSS v3.1 8.1,CWE-394 及概念验证参考:https://www.cve.org/CVERecord?id=CVE-2026-123158 (2026 年 11 月 6 日核查)
- Alpine Linux,“OpenSSH password authentication bypass”,2026 年 11 月 5 日,受影响配置、修复版本和规避措施:https://alpinelinux.org/posts/2026-11-05-openssh-password-authentication-bypass/ (2026 年 11 月 6 日核查)
- “sshole”,研究者的分析和浏览器实验室,musl 与 OpenSSH 的历史及利用条件:https://sshhole.com/ (2026 年 11 月 6 日核查)
- musl 提交 b3c4cc12,“crypt: return "*" instead of NULL on failure”,2012 年 8 月:https://git.musl-libc.org/cgit/musl/commit/?id=b3c4cc12 (2026 年 11 月 6 日核查)
- musl 提交 88bf5a8a,SHA-512 密钥的 256 字节上限,2012 年 8 月 29 日:https://git.musl-libc.org/cgit/musl/commit/?id=88bf5a8a (2026 年 11 月 6 日核查)
- OpenSSH
auth-passwd.c,10.6p1 标签,有缺陷的比较:https://github.com/openssh/openssh-portable/blob/V_10_6_P1/auth-passwd.c (2026 年 11 月 6 日核查) - OpenSSH
openbsd-compat/xcrypt.c,10.6p1 标签,pick_salt()和xcrypt():https://github.com/openssh/openssh-portable/blob/V_10_6_P1/openbsd-compat/xcrypt.c (2026 年 11 月 6 日核查) - Docker Hub,官方
alpine镜像,12,476,923,091 次拉取,使用规模数据:https://hub.docker.com/_/alpine (2026 年 11 月 6 日核查) - CISA Known Exploited Vulnerabilities 目录 JSON 源,2026 年 11 月 6 日核查 CVE-2026-123158(未列出):https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (2026 年 11 月 6 日核查)