OpenSSH Auth Bypass on Alpine Linux (*sshole, CVE-2026-123158): How 257-Byte Passwords Collide with musl crypt() and What 10.6p2 Fixes
Analyze how a 257+ byte password triggers a musl crypt() sentinel collision (*) in non-PAM OpenSSH (CVE-2026-123158), which Alpine Linux configurations are exposed, and how OpenSSH 10.6p2 closes the bypass.
A conceptual illustration showing OpenSSH 10.6p2's 4-character minimum hash-length shield blocking 257+ byte password payloads that trigger musl crypt()'s "*" return value, while allowing valid SSH public-key authentication below. This is not an actual console screenshot.
On November 5, 2026, the OpenSSH project and Alpine Linux released emergency security updates—Portable OpenSSH 10.6p2 and backported packages across all Alpine branches from 3.16 to edge—to fix *sshole (CVE-2026-123158, CVSS 3.1 8.1 High). On affected systems, a remote attacker who can reach the SSH port can log in to password-disabled accounts (whose /etc/shadow password field is set to *) on the very first attempt simply by sending any arbitrary password between 257 and 1,024 bytes long. OpenSSH 10.6p2 Release Notes, OX Security Analysis, Cycode Advisory
The key engineering takeaway: CVE-2026-123158 is not a cryptographic flaw or memory corruption bug—it is a sentinel collision where musl libc's crypt() error return value ("*") matches the /etc/shadow disabled-password marker ("*") inside a non-PAM sshd process. Systems running on glibc (Debian, Ubuntu, RHEL, Fedora), servers with UsePAM yes, and servers with PasswordAuthentication no are unaffected. However, any Alpine Linux host, container, or cloud image running the default non-PAM openssh-server with password authentication enabled must upgrade immediately and restart the running sshd daemon.
This guide examines the three-way interaction behind the bug, the exact five-condition exposure matrix, and the Alpine branch fix table based on the OpenSSH 10.6p2 release notes and technical advisories from Cycode, OX Security, and Konfirmity. It does not run exploit payloads against live SSH servers; the local Python checks below simulate the sentinel collision and audit host configuration state.
1. How Three Reasonable Decisions Collided to Create an Authentication Bypass
The vulnerability remained hidden since 2016 because each of the three underlying mechanisms behaves sensibly in isolation:
| Component | Original Design Intent | Component Behavior | Resulting Collision in CVE-2026-123158 |
| 1. musl libc crypt() Length Cap | Prevent CPU denial-of-service from hashing excessively long password inputs | Refuses passwords longer than 256 bytes (257–1,024 bytes) and returns the 1-character failure string "*" | The error return string ( "*") is byte-for-byte identical to the disabled-password marker in /etc/shadow |
| 2. OpenSSH Username-Enumeration Mitigation (2016) | Prevent timing side-channels that reveal whether an account exists or has a password | Even when a target account has no valid hash in /etc/shadow, non-PAM sshd borrows hash settings ($5$, $6$) from another local account, calls crypt(), and compares the output to the target's shadow field | Causes sshd to invoke musl's crypt() on a 257+ byte password even for a passwordless target account |
| 3. Alpine Linux * Shadow Entries (Alpine 3.16+, 2022) | Disable password logins while keeping the account unlocked for SSH public-key auth and doas | Stores * (rather than !) in the password field of /etc/shadow for key-only accounts and the default alpine user on official cloud images (3.18.11–3.24.3) | sshd compares crypt()'s failure output ("*") against the stored shadow field ("*"), sees an exact match, and grants login on the first attempt |
How OpenSSH 10.6p2 fixes the bug: Rather than changing musl's C library ABI, OpenSSH 10.6p2 rejects any stored password hash in /etc/shadow shorter than 4 characters. Because "*" is only 1 character long, it is rejected before any comparison with crypt() output can succeed.
2. Five-Condition Exposure Matrix: Who Is Affected vs. Safe
A host or container is vulnerable to CVE-2026-123158 only when all five conditions below are true simultaneously:
| Dimension | Exposed Configuration (All 5 Required) | Not Affected (Any 1 Prevents Exploitation) |
| 1. C Library & SSH Daemon | OpenSSH sshd built against musl libc (Alpine Linux, or custom OpenWrt/Buildroot/Yocto builds running OpenSSH) | glibc distributions (Debian, Ubuntu, RHEL, Fedora) or systems running Dropbear SSH (the OpenWrt default) |
| 2. PAM Integration | Runs without PAM (default in Alpine's openssh-server package, or openssh-server-pam with UsePAM no) | Runs with UsePAM yes, delegating password verification to PAM |
| 3. Password Authentication | PasswordAuthentication yes (the OpenSSH default) | PasswordAuthentication no (standard in Gitea, Gogs, Jenkins SSH agent, and key-only hardened hosts) |
| 4. Target Account Shadow Entry | At least one reachable account has * in /etc/shadow (e.g., key-only users, or the alpine user on Alpine cloud images). Note: root is protected if PermitRootLogin prohibit-password is active | Passwordless accounts are locked with ! (or !*) rather than * |
| 5. Donor Hash Presence | At least one other account on the system stores a real password hash (such as $5$ or $6$) in /etc/shadow | No account on the system has a valid local password hash, or only unsupported glibc yescrypt ($y$) hashes exist |
Pay special attention to official Alpine cloud images (3.18.11 through 3.24.3) on AWS and Azure: the default alpine user has * in /etc/shadow and passwordless doas privileges to run commands as root, turning a single 257-byte SSH password login into immediate root compromise.
3. Patched Alpine Versions and Immediate Remediation Commands
Alpine Linux backported the fix to every release branch back to 3.16 (including end-of-life branches). Systems on Alpine 3.15 or older do not receive a patched package and must either upgrade their base image or disable password authentication immediately.
| Alpine Branch | Fixed openssh Package Version | Deployment Note |
| edge | 10.6_p2-r0 or later | Ships upstream Portable OpenSSH 10.6p2 |
| 3.24 / 3.23 / 3.22 | 3.24: 10.3_p1-r2 / 3.23: 10.2_p1-r1 / 3.22: 10.0_p1-r11 | Backported minimum hash-length check |
| 3.21 / 3.20 / 3.19 | 3.21: 9.9_p2-r1 / 3.20: 9.7_p1-r6 / 3.19: 9.6_p1-r3 | Restart sshd after upgrading packages |
| 3.18 / 3.17 / 3.16 | 3.18: 9.3_p2-r4 / 3.17: 9.1_p1-r7 / 3.16: 9.0_p1-r6 | Releases prior to 3.16 receive no package fix; set PasswordAuthentication no or upgrade base OS |
Run the following commands on every Alpine host or container running sshd to verify exposure, apply the update, and check logs for indicators of compromise:
4. Simulating the musl crypt() Sentinel Collision and Auditing Configs in Python
The dependency-free Python script below models why a 257-byte password bypasses pre-10.6p2 non-PAM sshd, how the 4-character minimum hash check in 10.6p2 blocks it, and how to audit a host's /etc/shadow and sshd_config state.
Scope Verified Locally
Executing the script in a local Python environment verified five boundary cases:
- 257-byte password against a
*shadow entry:vulnerable_sshd_verify()returnsTruewhen a donor$6$salt is present.
- 12-byte password against a
*shadow entry: ReturnsFalsebecausecrypt()produces a normal hash string rather than"*".
- OpenSSH 10.6p2 minimum length check (
len < 4):patched_10_6p2_sshd_verify()rejects"*"immediately and returnsFalse.
- Account locked with
!: Even on pre-10.6p2,vulnerable_sshd_verify()returnsFalsebecause"*" != "!".
- Host exposure audit: Given
root: *,alpine: *,locked_svc: !, andadmin: $6$...underPermitRootLogin prohibit-password,audit_alpine_host_exposure()accurately pinpoints["alpine"]as the exposed account.
5. Production Hardening Checklist
- Rebuild and redeploy container images: Running
apk upgradeon live containers is temporary if your CI/CD pipeline still builds from an olderalpinelayer that installsopenssh-serverand sets a password viachpasswd. Rebuild and redeploy all affected images.
- Prefer
!over*for password-disabled accounts on non-PAM systems: Locking passwordless accounts withpasswd -l <user>(placing!in/etc/shadow) prevents any match withcrypt()'s"*"error sentinel.
- Shorten OpenSSH patch SLAs to hours, not weeks: In the 10.6p2 release notes, OpenSSH maintainers noted that because AI tools now rediscover reported vulnerabilities within hours, the project skips lengthy embargoes and ships fixes immediately. Treat OpenSSH point releases as same-day patches whenever SSH is network-reachable.
References
Last verified: November 7, 2026
Korean version:
OpenSSH 인증 우회 취약점(*sshole, CVE-2026-123158) 해부: 257바이트 비밀번호가 Alpine Linux를 뚫는 원리와 10.6p2 패치 점검하기