
OpenSSH与Alpine修复musl环境下空密码账户可被任意长密码登录的漏洞
来源标题:A 257-byte password matched '*' in OpenSSH on musl, traced through the code
Alpine Linux和OpenSSH于11月5日发布修复,堵住了一个可让足够长的密码登录无密码账户的漏洞。问题出在使用musl libc且禁用PAM的Linux系统,根源在sshd之外。OpenSSH同日发布10.6p2作为关键安全缓解措施,当时尚未分配CVE编号。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
11月5日,Alpine Linux 和 OpenSSH 针对一个漏洞发布了修复程序:只要密码足够长,就能登录到完全没有密码的账户。我们追踪了该漏洞在 OpenSSH 和 musl 中的确切路径,说明了 glibc 系统为何会安全失败,并给出检查你自己机器的命令。 Alpine Linux 于 2026 年 11 月 5 日发布安全公告,称远程攻击者可以通过 SSH 无密码登录,并告知用户更新 openssh 并重启 sshd。OpenSSH 同日发布 10.6p2,称其为针对禁用 PAM 的 musl libc Linux 系统的关键安全规避措施。OpenSSH 的说明中致谢 Wakefield 的 Özgün Kültekin,并表示根本原因在 sshd 之外,但 sshd 可以针对此问题进行加固。 公告发布时未分配 CVE。研究者本人在 sshhole.com 上的文章当天上午登上 Hacker News 榜首。 密码字段中的“”是 Unix 中表示任何密码都无法匹配的传统方式。仅使用密钥的用户、许多服务账户以及 Alpine 云镜像上的默认 alpine 用户都是这种情况。 密码哈希(如 $6$abc$...)以其自身的设置和盐值开头。sshd 将存储的哈希作为 setting 传给 crypt(),因此新生成的哈希会以相同格式输出。在 OpenSSH 的 auth-passwd.c 中,sys_auth_passwd 仅在存储值至少为两个字符时才执行该操作: if (authctxt->valid && pw_password[0] && pw_password[1]) salt = pw_password; encrypted_password = xcrypt(password, salt); 对于存储值为“”的情况,第二个字符是字符串结尾,因此 salt 保持为 NULL。随后 xcrypt() 调用 pick_salt(),后者遍历密码数据库,从第一个哈希以“$”开头的账户复制设置。如果找不到,则回退到两字符的 DES 设置“xx”。 pick_salt() 的存在是为了让针对没有真实哈希的用户的登录尝试耗时与真实用户大致相同。这可以防止攻击者通过时序判断哪些用户名存在,这类漏洞被追踪为 CVE-2016-6210。这是个好主意,也正是机器上任何位置的 $6$ 哈希最终会被用来哈希攻击者密码的原因。 musl 的 SHA-512 和 SHA-256 crypt 代码设置了一个密钥长度限制,并在密码过长时返回一个固定错误字符串: #define KEY_MAX 256 /* ... / return ""; 这两个文件的头部注释都说明了这一点:出错时返回“”。因此,一个 257 字节或更长的密码,使用借来的 $6$ 设置进行哈希后,会返回单字符字符串“”。 sys_auth_passwd 在新哈希等于存储值时接受登录。新哈希是“”,存储值也是“”,因此 strcmp() 返回 0,登录成功。auth_password() 会在这一切运行之前拒绝超过 MAX_PASSWORD_LEN(即 1024)的任何内容。这就是 257 到 1,024 范围的由来。 这两部分单独来看都没有错。返回“*”是在不返回 NULL 的情况下合理失败的方式,因为旧程序在 NULL 上会崩溃。借用盐值是合理的时序防御。但两者结合,就让一个错误字符串冒充了密码哈希。 Debian、Ubuntu、Fedora 以及大多数其他 glibc 发行版从 libxcrypt 获取 crypt()。其 make_failure_token() 在失败时写入“0”,而当 setting 本身以“0”开头时写入“1”。其 man 手册说明错误字符串将以“”开头,且不会与 setting 比较相等,并说明这样选择是为了让应用程序安全失败。“0”永远不会等于存储的“”,因此登录被拒绝。 通过 PAM 检查密码的系统走的是完全不同的代码路径。研究者的页面还将 OpenBSD 和 Dropbear 列为不受影响。 OpenSSH 提交“avoid password authn bypass”(4353fdc43e,11 月 4 日 UTC)在最终比较中增加了一条规则:短于四个字符的存储哈希永远无法匹配。 ret = encrypted_password != NULL && strlen(pw_password) >= MIN_PASSWORD_HASH_LEN && strcmp(encrypted_password, pw_password) == 0; Alpine 为从 3.16 到 edge 的每个分支发布了修复包。其表格中的几个:edge 10.6_p2-r0,3.24 10.3_p1-r2,3.23 10.2_p1-r1,3.22 10.0_p1-r11,3.21 9.9_p2-r1。Alpine 3.21.10、3.22.8、3.23.8 和 3.24.4 附带修复后的包,因此从这些版本构建的镜像已修复。 更新包不会替换已在运行的 sshd。请重启它。 Alpine 的公告称,其官方 tiny-cloud 镜像从 3.18.11 到 3.24.3 附带一个密码字段为“”且 doas 免密的 alpine 用户。一旦机器上存在任何 $6$ 哈希(例如有人为另一个用户设置了密码),这些镜像就会变得可利用。由于 doas 免密,以 alpine 登录后只需一条命令即可获得 root。 root 本身受到不同的默认值保护。无论哈希比较结果如何,除非 PermitRootLogin 设置为 yes,否则 sshd 拒绝 root 的密码登录。 在任何你不确定的 Linux 机器上以 root 身份运行这些命令。第一条命令显示 sshd 是否链接 musl,第二和第三条显示 PAM 和密码登录是否开启,最后一条并排列出“”账户和真实哈希。 ldd "$(command -v sshd)" | grep -i musl grep -ri '^ usepam' /etc/ssh/sshd_config /etc/ssh/sshd_config.d/ 2>/dev/null sshd -T | grep -i passwordauthentication awk -F: '$2==""{print "star:", $1} $2 ~ /^$[56]$/{print "hash:", $1}' /etc/shadow 如果你看到 musl、没有 UsePAM yes、passwordauthentication yes,并且至少有一行 star 和一行 hash,则该主机匹配所有条件。更新并重启: apk update && apk upgrade openssh rc-service sshd restart 如果现在无法更新,Alpine 的规避方法通过一个 drop-in 文件关闭密码登录,3.19 及更高版本默认会读取该文件。保持现有会话打开,并在关闭前确认密钥登录可用。
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。