sshole:Alpine 上十年 OpenSSH 免密登录洞
musl 的 crypt() 出错时返回「*」,而「*」正好是无密码账户在 shadow 里的标记;于是超长密码一来,本该永远登不进的账户就开了。
用同一个特殊字符串既表示「禁止登录」又表示「哈希失败」,十年后变成任意密码都能进。
事实
- 影响面:Alpine、OpenWrt、Buildroot、Yocto,以及大量基于 Alpine 的 Docker 镜像上的 OpenSSH(尤其未启用 PAM 的构建)。
- 攻击条件:密码登录开启、存在密码字段为 * 的账户(root、仅密钥用户、LDAP 用户常见),且机器上另有至少一个真实密码哈希($5$/$6$)。
- 手法:发送 257–1024 字节的超长密码;一次连接即可,无需爆破。
- 漏洞链路可追溯到 2012:musl 为兼容把 crypt 失败从返回 NULL 改成返回「*」,随后又给 SHA‑512 crypt 加了 256 字节上限。
解读
- 带内信令(in-band signaling)再次害人:错误码和业务标记共用一个字符串,比较相等就会「碰巧成功」。
- 对运维:立刻升级 openssh 并重启 sshd(只升包不重启不够);临时措施是 PasswordAuthentication no。
- 对产品基建:容器默认镜像安全假设要重新审视——「轻量发行版」不等于「默认更安全」。
讨论
- 正方评论区把机制讲得很清楚:目标是让 crypt(密码, 存下的哈希) 的返回值等于存下的哈希;错误返回「*」正好命中。
- 反方有人试了官方 Alpine 镜像复现失败——因为发行版已紧急更新;也有人说脆弱的是 musl,glibc 不受影响。
- 共识普遍同意:沉默的兼容性补丁比直接崩溃更危险,问题会藏十年。
- 有人联想到 LLM 系统同样依赖带内特殊字符串,担心同类设计债会在 agent 栈里重演。