Alpine and musl sshd without PAM: check for the OpenSSH 10.6p2 password bypass and fix it
OpenSSH below 10.6p2 on musl Linux without PAM can accept any long password for accounts stored as *. Check your sshd, upgrade, restart, verify the running build.

Drafted with AI assistance, then checked and edited by a human engineer · How we write
Short answer
OpenSSH below 10.6p2 on musl Linux (Alpine) with PAM off can accept any password of 257 to 1024 bytes for accounts whose shadow field is *, such as root on recent Alpine base images (CVE-2026-123158). Upgrade to 10.6p2 or your Alpine branch's fixed openssh package, restart sshd, and confirm the running daemon is the new build. Until then set PasswordAuthentication no.
| CVE | CVE-2026-123158, CVSS 3.1 8.1, published 2026-11-05 |
|---|---|
| Fixed in | OpenSSH 10.6p2 (2026-11-05); Alpine fixed packages back to 3.16, for example 3.24 openssh 10.3_p1-r2 |
| Affected setup | musl libc, sshd without PAM (or UsePAM no), PasswordAuthentication on, an account stored as * plus one account with a $5$ or $6$ hash |
| Not affected | glibc distros, UsePAM yes with openssh-server-pam, PasswordAuthentication no, locked ! accounts |
| Mitigation | PasswordAuthentication no, verified with sshd -T |
| Our lab | 13 throwaway host profiles: 10/10 unsafe cases flagged, 10/10 cleared by the fix, 10/10 normal cases unchanged |
OpenSSH below 10.6p2 on musl Linux, built or run without PAM, can accept any password between 257 and 1024 bytes for an account whose shadow field is *, which on recent Alpine images includes root (CVE-2026-123158). Upgrade to OpenSSH 10.6p2 or your Alpine branch's fixed openssh package, restart sshd, and check that the running daemon is the new build; until you can, set PasswordAuthentication no.
This guide is for protecting servers and images you own or maintain.
Am I affected?
The OpenSSH 10.6p2 notes scope it to Linux with musl libc and PAM disabled. The Alpine advisory adds what must be true on the host. All of these at once:
| Condition | How to check |
|---|---|
| musl libc | ldd --version 2>&1 | head -1 prints musl |
| OpenSSH below 10.6p2, or an Alpine revision below the fixed one | apk info -v openssh-server |
No PAM: plain openssh-server, or openssh-server-pam with UsePAM no | sshd -T | grep -i usepam |
| Password login reachable for some user | sshd -T | grep -i passwordauthentication, plus sshd -T -C user=<name>,host=<h>,addr=<ip> for users in Match blocks |
An account stored as * and another with a $5$ or $6$ hash | awk -F: '$2=="*"{print "star:",$1} $2~/^\$[56]\$/{print "hash:",$1}' /etc/shadow |
# run as root on each Alpine host or inside each container that runs sshd
ldd --version 2>&1 | head -1
apk info -v openssh-server openssh-server-pam 2>/dev/null
sshd -T | grep -iE '^(passwordauthentication|usepam) '
awk -F: '$2=="*"{print "star:",$1} $2~/^\$[56]\$/{print "hash:",$1}' /etc/shadow
Fixed Alpine revisions, from the advisory:
| Branch | Fixed 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 (end of support 2026-11-01) | 9.9_p2-r1 |
| 3.20 to 3.16 (unsupported) | 9.7_p1-r6, 9.6_p1-r3, 9.3_p2-r4, 9.1_p1-r7, 9.0_p1-r6 |
Alpine lists as not affected: glibc distributions, openssh-server-pam with UsePAM yes, PasswordAuthentication no, accounts locked with !, hosts with no $-style hashes, and containers that do not run sshd. Official tiny-cloud images from 3.18.11 to 3.24.3 ship an alpine user stored as * with passwordless doas, so check those first.
Fix it now
# Alpine host or long-running container
apk upgrade -U -a
rc-service sshd restart # systemctl restart sshd on systemd-based musl distros
apk info -v openssh-server # must match the fixed revision for your branch
For images, rebuild from a base that already has the fix (Alpine 3.21.10, 3.22.8, 3.23.8, 3.24.4 or later per the advisory) or add apk upgrade --no-cache openssh-server to the Dockerfile, then redeploy every container. Other musl distributions: take OpenSSH 10.6p2 or your vendor's patched package.
The package on disk is not what serves logins. Alpine notes that upgrading alone leaves the old daemon running. Check the listener process:
pid=$(cat /var/run/sshd.pid)
ls -l /proc/$pid/exe # "(deleted)" at the end means the old binary is still running
After the restart the link points at /usr/sbin/sshd with no (deleted) suffix, and a fresh login over a key still works.
Why it is dangerous
Without PAM, sshd checks a password itself in sys_auth_passwd():
client password --> sshd (PasswordAuthentication, no PAM)
1. read the stored shadow field for the user root -> "*"
2. field too short to hold a salt, so sshd borrows salt from another account's $6$ hash
a salt from another account
3. crypt(password, salt) in musl password > 256 bytes -> returns "*"
4. strcmp(result, stored) == 0 "*" == "*" -> accepted
According to the Alpine advisory, musl's crypt() returns * as its error value when the key is longer than 256 bytes, and * is also the shadow marker for an account with no password. sshd caps passwords at 1024 bytes, which gives the 257 to 1024 range in the CVE record. The error string and the "no password" marker are the same bytes, so step 4 compares two failure values and calls it a match. The CVE record files this under CWE-394, an unexpected return value.
The input reaches the comparison before any proof of identity, so an attacker on the network gets a shell as an account that was meant to accept keys only. On recent Alpine images root is stored as *, and in tiny-cloud images the alpine user has passwordless doas. OpenSSH places the root cause outside sshd. The fix commit closes it inside sshd anyway: it refuses any stored entry shorter than four characters (MIN_PASSWORD_HASH_LEN 4), so a short marker can never equal a hash, and it also changes the crypt wrapper in openbsd-compat/xcrypt.c.
Reproduce
Our lab, labs/security/openssh-auth-check-lab/openssh-auth-check-lab.mjs, is a read-only self-check in Node 22.13.1. It needs no network, no sshd and no root. We ran it on this Mac on 2026-11-06 at 11:10 UTC. Part A builds 13 throwaway host profiles (an ssh -V line, a libc name, an sshd_config with drop-ins and Match blocks) and reads each config the way sshd does: first value wins, Include files are read where the line sits, defaults are PasswordAuthentication yes and UsePAM no. Part B runs the old and new password rule side by side, with a Node scrypt hash standing in for crypt().
All six unsafe profiles came back AFFECTED. Three are config traps worth checking on your own hosts: passwords off globally but on in Match Group staff, a drop-in 50-cloud.conf that turns passwords on before the main file's no, and a yes line early in the file that a later no does not undo. In Part B the old rule accepted all four short stored entries (!, *, x, !!) once the computed result equalled the entry.
Experiment
The same 20 inputs ran against the unsafe and the fixed state:
| Input | Unsafe | Fixed |
|---|---|---|
| 6 affected host profiles (10.6p1, 9.9p2, apk 10.4_p1-r0, Match block, drop-in, first line wins) | AFFECTED | ok |
glibc with UsePAM yes, already 10.6p2, 10.7p1, keys only, commented-out yes, musl with UsePAM yes, macOS | ok | ok |
| right password on a real hash | true | true |
| wrong password, and a 1024-character wrong password | false | false |
short stored entry !, *, x, !! with a matching result | accepted | refused |
The new length guard looks at the stored entry, never at the typed password, so the 1024-character password against a real hash was refused by both rules.
Fix
The fixed state is one of two changes, applied to the copy of each host: the upgrade to 10.6p2, or PasswordAuthentication no at the top of sshd_config and in every drop-in and Match block. In code, the rule becomes:
/* auth-passwd.c, before */
return encrypted_password != NULL &&
strcmp(encrypted_password, pw_password) == 0;
/* after: a stored entry shorter than 4 characters is never a hash */
ret = encrypted_password != NULL &&
strlen(pw_password) >= MIN_PASSWORD_HASH_LEN &&
strcmp(encrypted_password, pw_password) == 0;
Report

| Count | Result |
|---|---|
| Unsafe inputs flagged or accepted on the old version | 10/10 |
| Blocked by the fix | 10/10 |
| Normal inputs unchanged | 10/10 |
| This Mac | OpenSSH_10.2p1, LibreSSL 3.3.6, darwin libc: below 10.6p2 but not the affected setup |
What our lab does not prove: it never calls musl's crypt() and never makes a real collision, so it tests the guard, not the bug. Its version check compares upstream numbers only, so it would flag a 9.9p2 host that Alpine already patched as 9.9_p2-r1; use the advisory table for that. It does not look for the * account plus $6$ hash pair Alpine requires. Alpine also lists ! locked accounts as not affected in practice; our rule rows include ! only to show the new guard refuses every short entry.
If you cannot update today
The one official mitigation is turning password login off. On Alpine 3.19 and later, sshd reads /etc/ssh/sshd_config.d/ from an Include near the top, and the first value wins, so a drop-in overrides a later yes in the main file. A PasswordAuthentication yes inside a Match block still applies to its users, so remove those too.
# keep this session open and test key login in a second one before you log out
printf 'PasswordAuthentication no\n' > /etc/ssh/sshd_config.d/00-no-passwords.conf
sshd -t && rc-service sshd restart
| Check | Expected |
|---|---|
sshd -T | grep -i passwordauthentication | passwordauthentication no |
sshd -T -C user=<name>,host=<h>,addr=<ip> for each user or group in a Match block | passwordauthentication no |
From another machine you own: ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no you@host | Permission denied (publickey) |
The same version-then-running-process habit runs through our APT signature check, the PHP-FPM check and the Node.js Brotli check. If you want alerts on odd logins while you patch, the 3 a.m. login rule is a cheap start.
Timeline and sources
| Date | Event |
|---|---|
| 2026-10-06 | OpenSSH 10.6 and 10.6p1 released |
| 2026-11-01 | Alpine 3.21 reaches end of support |
| 2026-11-05 | OpenSSH 10.6p2 released; Alpine advisory and fixed packages back to 3.16; CVE-2026-123158 published (CVSS 3.1 8.1); reported by Özgün Kültekin of Wakefield |
| 2026-11-05 | The Hacker News covers the release |
| 2026-11-06 | Our lab run, 11:10 UTC |
On exploitation: CISA's enrichment on the CVE record marks it as a public proof of concept, not automatable. We found no official report of exploitation in the wild. The Alpine advisory still asks you to review logs on any host where password login was reachable.
My take: two different meanings shared one string, * as "crypt failed" and * as "this account has no password", and a plain equality check could not tell them apart. On your own hosts, the fastest wins are the upgrade plus restart and keys only for every account; on images, the base tag is the thing to bump.
Frequently asked questions
My Alpine box shows openssh 9.9_p2. Is that below 10.6p2 and therefore affected?
Not necessarily. Alpine backported the fix, so 3.21 is fixed at 9.9_p2-r1 and 3.24 at 10.3_p1-r2. Compare the full package revision from apk info -v openssh-server with the table in the Alpine advisory, not the upstream version number.
I upgraded the package. Why do I still need to restart?
Alpine's advisory says the upgrade does not replace the daemon that is already running. Restart sshd, then check that /proc/<pid>/exe for the sshd listener no longer ends in (deleted).
Do I need to check logs as well?
If password login was reachable on an affected host, yes. Alpine's advisory says to look in /var/log/messages for Accepted password lines on accounts that have no password, and to review doas use, new users, files and authorized_keys. If you cannot rule out such a login, treat the machine as compromised.
Sources
- OpenSSH release notes, 2026-11-05: OpenSSH 10.6p2openssh.org
- Alpine Linux, 2026-11-05: openssh-server password authentication bypassalpinelinux.org
- CVE-2026-123158 record (CVSS 3.1 8.1)cve.org
- openssh-portable commit 4353fdc: avoid password authn bypassgithub.com
- sshd_config(5) manual: PasswordAuthentication, Include, Matchman.openbsd.org
- The Hacker News, 2026-11-05: OpenSSH issues emergency fix for flawthehackernews.com