Sshole
3002 points
• 7 days ago
• Article
Link
在基于 musl 的 Linux 发行版(如 Alpine 、 OpenWrt 和 Yocto)中,OpenSSH 长期存在一个安全漏洞。该漏洞源于 libc 处理身份验证错误的方式,与 OpenSSH 为缓解计时攻击所采取的措施之间的相互作用。十多年来,配置为使用单个星号作为密码占位符的账户(这是希望仅依赖 SSH keys 的用户常见做法)无意中接受了任何超过 256 个字符的密码。该缺陷使攻击者能够绕过安全防护,获取那些本应明确禁止基于密码登录的账户的未授权访问。
该问题可追溯到 2012 年。当时 musl 库修改其 crypt 函数以避免系统崩溃。从历史来看,如果身份验证函数无法计算哈希值,它会返回一个 NULL 指针,许多程序因为未检查这一情况而会崩溃。 musl 通过改为返回一个字面星号字符来修复这一点:它假设由于不存在任何有效哈希能与字面星号匹配,结果会被当作密码验证失败。然而,这一修复与同时引入的 256 字节密码长度限制发生了重叠,用于防止拒绝服务攻击。当密码长度超过限制时,musl 的 crypt 函数会失败并返回同样的星号。
更复杂的是,OpenSSH 在 2016 年引入了一套用于防止计时攻击的缓解策略。当用户尝试登录一个不存在的账户时,OpenSSH 通常会立即拒绝登录。为防止攻击者根据响应时间推断有效用户名,OpenSSH 开始从系统中存在的账户里随机挑选一个有效 salt,以执行"伪哈希"计算。这样可以确保服务器在拒绝有效用户和无效用户时耗时一致。结果,OpenSSH 反而不经意间提供了触发 musl 漏洞所需的关键条件。
当用户试图针对一个密码域为星号的账户进行身份验证时,OpenSSH 会判断需要执行用于隐藏计时的哈希计算。它会从系统中另一个账户取得有效 salt,并对用户输入的内容进行哈希处理。如果输入长度超过 256 字节,musl 的 crypt 函数就会返回星号。由于被访问账户的密码字段同样是星号,比较结果会被判定为匹配。于是,任何长度超过 256 个字符的密码都能充当锁定账户的"通用密钥",包括许多默认的 Alpine Linux 云映像中的 root 用户。
由于大多数标准 Linux 发行版依赖 glibc 和 PAM,它们在处理这类身份验证流程时方式不同,且不会受到相同的交互影响,因此该漏洞基本被忽视了十年。该问题主要影响那些在没有 PAM 的情况下使用 musl 的系统——这种配置在轻量级容器和云环境中很常见。建议用户立即升级到 OpenSSH 10.6p2,或在配置文件中将 PasswordAuthentication 设置为 no 以降低风险。同时建议检查日志中是否出现异常的长字符串密码输入尝试,以判断系统是否已经被入侵。
A long-standing security vulnerability has existed within OpenSSH on musl-based Linux distributions, such as Alpine, OpenWrt, and Yocto, stemming from an interplay between how libc handles authentication errors and how OpenSSH mitigates timing attacks. For over a decade, accounts configured with a single asterisk as a password placeholder—a common setting for users intended to rely solely on SSH keys—have inadvertently accepted any password exceeding 256 characters. This flaw allows attackers to bypass security measures and gain unauthorized access to accounts that were explicitly intended to prohibit password-based logins.
The origins of this issue date back to 2012, when the musl library modified its crypt function to prevent system crashes. Historically, if an authentication function could not compute a hash, it would return a NULL pointer, causing many programs to crash because they failed to check for this condition. Musl addressed this by returning a literal asterisk character instead, assuming that since no valid hash could ever match a literal asterisk, the result would be interpreted as a failed password. However, this fix coincided with the introduction of a 256-byte limit on password lengths to protect against denial-of-service attacks. When a password exceeds this length, musl's crypt function fails and returns that same asterisk.
Compounding this is an OpenSSH mitigation strategy implemented in 2016 designed to prevent timing attacks. When a user attempts to log into a non-existent account, OpenSSH typically rejects the login immediately. To prevent attackers from guessing valid usernames based on response times, OpenSSH began picking a random valid salt from an existing account to perform a fake hash calculation. This ensures that the server takes the same amount of time to reject both valid and invalid users. By doing so, OpenSSH unintentionally provided the necessary ingredients to trigger the musl vulnerability.
When a user attempts to authenticate against an account locked with an asterisk, OpenSSH identifies that it needs to perform a timing-hiding hash. It grabs a valid salt from another account on the system and hashes the user's provided input. If that input is longer than 256 bytes, the musl crypt function returns an asterisk. Since the account being accessed also contains an asterisk as its password field, the comparison results in a match. This effectively allows any password longer than 256 characters to act as a universal key for locked accounts, including the root user in many default Alpine Linux cloud images.
This vulnerability largely went unnoticed for ten years because most standard Linux distributions rely on glibc and PAM, which handle these authentication flows differently and do not suffer from the same interaction. The issue specifically targets systems that use musl without PAM, a configuration common in lightweight containers and cloud environments. Users are advised to immediately update to OpenSSH 10.6p2 or set PasswordAuthentication to no in their configuration files to mitigate the risk. Checking logs for unusual, long strings in password entry attempts is recommended to determine if a system has already been compromised.
752 comments • Comments Link
• 该漏洞源于在使用 `musl` libc 时,`sshd` 中存在身份验证绕过问题,触发原因在于该库对 `crypt()` 的实现并不符合常规。该实现会在错误条件下返回标记字符串("" 或 "+"),而不是返回 `NULL`,从而被 OpenSSH 误判为匹配成功的密码哈希。
• 利用该漏洞需要满足一组特定且较为苛刻的条件:必须使用 `musl` libc,禁用 PAM,启用基于密码的身份验证;目标账户的密码哈希必须为 ``;同时还需要在同一个连接中进行多次尝试。
• 批评者认为,`musl` 开发人员在失败时返回任意字符串而不是 `NULL`,违反了 `crypt()` 的 POSIX 规范,本质上是为了避免程序崩溃而牺牲了应有的错误信号传递机制。
• 对带内信号(In-band signaling)的依赖——例如用 "*" 或 "!" 这类无实际意义的字符串作为元数据——被视为 Unix-like 环境中一种系统性的设计缺陷。此类缺陷往往会引发危险的歧义,并导致安全疏忽。
• C 语言缺少现代类型系统和显式结果处理机制,使得这些长期存在的错误类别得以延续。许多旧式 C API 习惯用哨兵值(Sentinel values)表示错误,而这些值很容易被调用方忽略或被处理逻辑错位。
• 此次发现也体现出 Agent swarms 与 AI 辅助的安全研究在定位复杂漏洞链方面的效率不断提升。此类漏洞链往往会被人工审查遗漏,尽管这些报告有时会存在文风不一致或明显带有"AI 生成"的口吻。
• 面向系统加固的建议包括:彻底禁用基于密码的身份验证,使用带硬件支持的密钥(Hardware-backed keys),并避免将 SSH 服务暴露在公共互联网上——不论特定的 libc 或操作系统配置是否被认为足够安全。
• 关于 `musl` 在错误处理上选择"掩盖"的做法,业界存在争议:这究竟是为兼容遗留系统而做出的务实但带风险的妥协,还是一种更根本的错误决策——即优先避免崩溃而非优先保证安全。
• 一些人质疑该漏洞在真实场景中的严重程度,认为它在现代标准容器环境中难以复现。但对于配置错误的遗留系统或专用系统而言,它依然是一个严重风险。
• 整体讨论反映出对软件漏洞"永恒"状态的更广泛挫败感,以及在维护陈旧 API 契约与推动向更安全、更结构化的编程范式演进之间持续存在的紧张关系。
本次讨论聚焦于与 `musl` libc 和 OpenSSH 相关的复杂身份验证绕过问题,并将其作为一个关于带内信号处理以及静默错误处理风险的案例研究。各方形成了强烈共识:根本原因在于 C 语言遗留的错误报告模式——必须通过 `NULL` 或错误字符串等哨兵值来表达失败,而这些值极易在危险情况下被误解。尽管部分参与者强调让遗留系统继续运行的务实必要性,仍有不少人指出,该漏洞再次说明了使用现代编程语言以及通过强制显式错误处理的结构化数据格式的优势。最终,这次对话凸显了安全研究正从"被动观察"向"AI 增强的发现"方式转变的趋势,并引发人们对未来软件维护以及在关键系统基础中持续累积的技术债务的质疑。 • The vulnerability arises from an authentication bypass in `sshd` when using `musl` libc, triggered by a non-standard `crypt()` implementation that returns a sentinel string ("" or "+") instead of `NULL` during error conditions, which is then misinterpreted by OpenSSH as a valid password hash match.
• Exploiting this requires a specific, narrow set of conditions: `musl` libc, no PAM, password authentication enabled, and a target account with `` as its password hash, necessitating multiple attempts within the same connection.
• Critics argue that `musl` developers violated the POSIX specification for `crypt()` by returning arbitrary strings rather than `NULL` on failure, essentially prioritizing the avoidance of program crashes over correct error signaling.
• The reliance on in-band signaling—using meaningless strings like "*" or "!" as metadata—is identified as a systemic design flaw in Unix-like environments that frequently leads to dangerous ambiguity and security oversights.
• A lack of modern type systems and explicit result handling in C contributes to these persistent classes of errors, as many older C APIs default to using sentinel values for errors, which can be easily ignored or misaligned by calling code.
• The discovery process highlights the increasing efficacy of agent swarms and AI-assisted security research in identifying complex vulnerability chains that manual review might overlook, though these reports often suffer from stylistic inconsistencies or "AI-generated" tone.
• Recommendations for hardening include disabling password authentication entirely, using hardware-backed keys, and avoiding the exposure of SSH services to the public internet, regardless of whether a specific libc or OS configuration is considered secure.
• Debate exists over whether `musl`'s decision to mask errors was a pragmatic, albeit risky, compromise for legacy compatibility or a fundamental error in judgment that prioritizes avoiding program crashes over security.
• Some express skepticism regarding the severity of the bug in real-world scenarios, noting that it is difficult to reproduce on modern, standard container environments, though it remains a severe risk for misconfigured legacy or specialized systems.
• The discussion reflects broader frustrations with the "eternal" state of software vulnerabilities and the ongoing tension between maintaining ancient API contracts and evolving towards safer, more structured programming paradigms.
The discussion centers on a sophisticated authentication bypass involving `musl` libc and OpenSSH, serving as a case study for the dangers of in-band signaling and silent error handling. There is a strong consensus that the root cause lies in C's legacy error-reporting patterns, which necessitate sentinel values like `NULL` or error strings that can be dangerously misinterpreted. While some participants defend the pragmatic necessity of keeping legacy systems running, many highlight how this bug illustrates the superiority of modern programming languages and structured data formats that enforce explicit error handling. Ultimately, the conversation underscores a shift in security research toward AI-augmented discovery, raising questions about the future of software maintenance and the mounting technical debt within the foundation of critical systems.