安全攻防
morning
OpenSSH/musl 口令认证绕过漏洞底层分析及修复版本对比剖析
2026-11-11
1 阅读
约10分钟阅读
先知社区
字号:
前言 11 月初 sshhole 这份披露出来的时候,五步根因链读下来,最不踏实的是第 4 步,也就是"触发前需要一次错误口令尝试"那句。披露把原因归给静态缓冲区被覆盖,可怎么读都觉得只说了一半。手边刚好有几份上游源码包,就把 10.6p1 / 10.6p2 / 7.2p2 / 7.3p1 的 auth-passwd.c 和 xcrypt.c 拉出来逐行对了一遍,又把 musl 1.2.6 的 crypt 后端单独编了个探针,跑了下长度差分。 第一版写完之后,又在隔离容器里补了一轮实测:两个只绑 127.0.0.1 的 sshd 实例,一个已回移修复、一个源码未打补丁,把链条端到端跑了一遍。这一轮证伪了初稿两处判断,也把"版本号能不能当判定依据"钉死了,分别写在第七节和第十一节。证据分级的口径跟着改过,A 级现在包含本地实测。 后半篇是给运维看的:几条只读自查命令、止损顺序、能写成规则的检测点。所有 file:line 都指向解包后的源码正本, sed -n 能直接复核。复现部分只给到环境、判据和日志证据,PoC 与握手脚本不展开。 一、事件概述 2026 年 11 月初,一份公开披露描述了 OpenSSH portable 在 musl libc 上的口令认证绕过:未启用 PAM 构建的 sshd,对影子字段恰好是单个 * 的账户,接受长度在 257 到 1024 字节之间的任意口令,前提是在同一条 TCP 连接内先失败一次。披露方给这个问题起名 sshhole。 证据按 A / B / C 分:A 是本机跑过或逐字读自源码正本的(含第七节那轮容器实测),B 是披露与公告里没法复现的结论,C 是单源未核实、文末单列。所有 file:line 都指向第二节表格里那几份 tar 包解包后的实物, sed -n 'X,Yp' 直接能复核。 几条结论先摆在这儿,依据后面逐条摊开。 链条在源码层面成立,五环都能落到具体行(第三节);这轮隔离环境里端到端跑通了,同连接第二次 Accepted password (第七节)。 披露对"为什么需要第二次尝试"的解释缺了决定性一环。让第二次不再被破坏的,是 pick_salt() 开头那两行的静态缓存短路(第四节)。 窗口只对借到 $5$ 或 $6$ 前缀的情况打开,实测可利用长度 257–1024 字节(第五节、第七节)。 10.6p2 的修复由两处互相独立的改动组成,只回移植其中一处会留下另一半(第八节)。 这条分支从 7.3p1 起存在;7.2p2 在同一位置写死 DES 盐 xx ,不在窗口内(第六节)。 版本号不能作为受影响判定依据 :Alpine 10.3_p1-r2 落在区间内却已回移修复,判定要落到二进制行为(第七节)。 触发条件:七条合取 披露把触发条件写成"musl 无 PAM + 影子字段是 * + 超长口令",三个词。真到主机上要凑齐的远不止这些。七条合取、各自的源码依据、还有运维能直接跑的只读自查,图1 里都摆开了。 第 5 条最容易看漏,展开说一下。 pick_salt() 只认以 $ 开头、且中间还有 $ 的影子值( xcrypt.c:85-87 ),借来的前缀决定后面走哪条 crypt 后端,而长度阈值只写在 sha256 与 sha512 两条后端里。借到 md5 盐时, crypt_md5.c:182 的上限是 30000 字节,1024 字节的口令根本触不到;借到 blowfish 盐时, crypt_blowfish.c:766 直接调 BF_crypt ,没有任何 key 长度拒绝;借到传统 DES 盐(也就是 pick_salt() 遍历失败时 xcrypt.c:80 写下的 xx )时,DES 把 key 截到 8 字节,多长的口令都是同一个结果。这三条都在第五节里实测过。 二、取证材料与证据分级 材料 sha256 用途 src/openssh-10.6p1.tar.gz a9dc9565dffe8640f64d863cd29a32bc4a3dbdec0566a7fc44c5d6ee767d5f39 漏洞版本正本,第三节/第四节的行号来源 src/openssh-10.6p2.tar.gz 9ae44872090cf2f06b3cf9ff147e7f16f309671340763ed2f99248398ae29d5b 修复版本正本,第八节的来源 src/openssh-7.2p2.tar.gz a72781d1a043876a224ff1b0032daa4094d87565a68528759c1c2cab5482548c 回归点下界,第六节 src/openssh-7.3p1.tar.gz 3ffb989a6dcaa69594c3b550d4855a5a2e1718ccdde7f5e36387b424220fbecc 回归点上界,第六节 src/openssh-7.5p1.tar.gz 9846e3c5fab9f0547400b4d2c017992f914222b3fd1f8eee6c7dc6bc5e59f9f0 中间版本抽样,确认形态未再变化 src/musl-1.2.6.tar.gz d585fd3b613c66151fc3249e8ed44f77020cb5e6c1e635a616d3f9f82460512a crypt 后端与 passwd 读取,第五节的编译对象 六份包的本机 shasum -a 256 结果汇总在 src/sshhole-sources.sha256 。解包目录 src/openssh-10.6p1/ 这些只拿来读,一个字没改;代码卡片是生成器从这些目录逐字切出来的。 探针与输出: 文件 sha256 内容 sshhole-evidence/probe.c 47106d5f4e505660d3b3558f18f4ab5ffbfba1d22daf0e9856b5d8b3526c4077 四条 sha/md5/blowfish 后端的长度差分探针 sshhole-evidence/des_probe.c 034d1e4fb8a319d608b741e528f0fbbc18b5a1012701ada9e4386222d4ee31ea DES 后端探针 sshhole-evidence/run-probes.sh c5e07a9f3a9058a06415c13194eb20744fd6e3167b21488582bae3e02fa038eb 编译与运行入口 sshhole-evidence/probe-output.txt 2f4050183e932c7ab8e1c7662f5de328a98f5d5cc8e4eef7d43e6b4a6af6eae7 首轮输出,第五节表格来源 sshhole-evidence/des-output.txt 85612d40b9f0b936f0fbed6d472b954f40f01c3c327ff06b4000aa2f12b8b726 DES 输出 编译环境是 Apple clang 21.0.0,目标 arm64-apple-darwin25.3.0。2
这篇文章对您有帮助吗?
订阅66必读
每日精选科技资讯,直达你的邮箱