OpenSSH 10.6p2, released on 5 November 2026, works around a password bypass in sshd on Linux systems that use musl libc without PAM, with Alpine as the main case. What we found: the package count badly overstates exposure, the deciding question is whether a musl sshd is actually running, and some of the conditions that matter cannot be seen from telemetry at all.
TL;DR
The bug was reported to OpenSSH on 4 November by Özgün Kültekin of Wakefield and published the next day as “sshhole”, alongside OpenSSH 10.6p2 and an Alpine advisory. No CVE had been assigned at the time of writing. Within hours, we swept the environments we monitor to find out who was really exposed.
- Any password of 257 to 1024 bytes logs into accounts whose password field is
*. It takes one connection and no retries. On Alpine,*is normal for key-only users and for root. - Five conditions must all hold, not three. musl, no PAM,
PasswordAuthentication yes, an account with*, and at least one other local account with a$5$or$6$hash. - The package count is misleading. About 98 environments ship Alpine
openssh-server. Most of them are debug, CI or VM images that never start sshd. - Runtime evidence cut the list down fast. Of 471 groups of containers running sshd, 331 were glibc and not affected, 38 were musl and 102 could not be classified.
- Check the Alpine branch fix, not only 10.6p2. Alpine backported the fix to every branch back to 3.16, so the right fixed version depends on your release.
- Some of what decides exposure is invisible. Reachability, the effective sshd configuration and whether a login was a bypass cannot be confirmed from telemetry alone.
Fixed versions (Alpine openssh): 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, 3.20 9.7_p1-r6, 3.19 9.6_p1-r3, 3.18 9.3_p2-r4, 3.17 9.1_p1-r7, 3.16 9.0_p1-r6.
What is sshhole?
In one sentence: musl’s error value for a failed hash is the same string Alpine stores for accounts with no password.
Without PAM, sshd checks a password by hashing it with crypt() and comparing the result to the stored field. For an account whose field is too short to hold a salt, such as *, sshd borrows the salt of another real account. That behavior dates to the 2016 fix for a timing attack (CVE-2016-6210).
Since August 2012, musl’s crypt() returns the literal string * when hashing fails, instead of the NULL that POSIX requires. Its SHA-512 implementation fails for passwords longer than 256 bytes. So a long password hashes to *, the stored field is *, and the comparison matches.
Neither piece is wrong on its own. Together they let an attacker in. OpenSSH says the root cause lies outside OpenSSH, and 10.6p2 now refuses a match against a stored field too short to be a real hash.
Who is affected?
Only systems where all five conditions hold.
- musl: Alpine, plus OpenWrt, Buildroot and Yocto builds that run OpenSSH.
- No PAM: Alpine’s
openssh-serverpackage is built without PAM.openssh-server-pamis affected only withUsePAM no. - Password logins on:
PasswordAuthentication yesis the OpenSSH default. - A
*account: root ships asroot:*since about Alpine 3.16, key-only users created withadduser, directory-service users without a shadow entry, and thealpineuser in tiny-cloud images. - One account with a real hash: a single
$5$or$6$password anywhere on the machine makes every*account reachable.
Root has one safeguard. The default PermitRootLogin prohibit-password blocks it, unless another * account can escalate. The tiny-cloud images from 3.18.11 through 3.24.3 ship exactly that: an alpine user with * and passwordless doas.
Not affected: glibc Linux such as Debian and Ubuntu, any system using PAM for passwords, non-Linux systems, Dropbear, accounts locked with !, cloud-init images that write PasswordAuthentication no, and containers that do not run sshd.
Why does the SBOM overcount?
Because a package on disk is not a daemon on a port. Our first pass used image scans. About 98 environments had Alpine openssh-server, and the highest version we saw was 10.3_p1-r1, one revision below the 3.24 fix. On paper, every one was vulnerable.
In practice, most were not close. Eighteen environments had the package only through a popular network-debugging image that never starts sshd. Many more had it only in VM or AMI images, where the package came from a nested container. Workload-level flags like internet ingress describe the workload, not port 22, so they cannot prove SSH exposure either.
The practical takeaway: an SBOM hit is a reason to look, not a reason to page someone.
How can exploitation be detected?
Look for successful password logins to accounts that have no password. On Alpine, search /var/log/messages for Accepted password for followed by root, alpine or any key-only user. That line should never appear for a * account.
If you find one, or cannot rule one out, treat the machine as compromised. Review doas use, new users, changed authorized_keys files and recently written files on exposed hosts.
What should security teams do now?
- Find running musl sshd first. Run
sshd -T | grep -i passwordauthenticationon each one to see the effective setting. - Upgrade and restart. Run
apk upgrade -U -a, thenrc-service sshd restart. Upgrading alone leaves the old daemon running. - If you cannot upgrade, turn off password logins. Set
PasswordAuthentication noand restart sshd. Confirm key login works first. - Fix the accounts. Lock unused accounts with
!and remove passwordlessdoasfrom thealpineuser. - Hunt for
Accepted password foron passwordless accounts on anything that was reachable. - Don’t forget devices. OpenWrt, Buildroot and Yocto systems running OpenSSH need the same check.
How can Upwind help?
sshhole showed how far a package count is from real exposure. What decided risk was which containers actually ran sshd, on which C library, and whether anyone logged in. Upwind combines image SBOMs with runtime process data, so teams can move from “who has the package” to “who runs the daemon” in one place and focus on the short list.
Upwind also watches what happens after a login at runtime: new shells, privilege escalation through doas, and changes to accounts and keys.
To learn more about how Upwind ties vulnerable packages to what is actually running, schedule a demo.
