Skip to main content
6 min read

Alpine OpenSSH Password Bypass (CVE-2026-123158): Check and Fix

Alpine OpenSSH Password Bypass (CVE-2026-123158): Check and Fix

If you run sshd on Alpine Linux, upgrade openssh and restart sshd now:

apk upgrade -U -a && rc-service sshd restart

On 2026-11-05, Alpine published fixed openssh packages for every branch from 3.16 to edge, and OpenSSH released 10.6p2. The bug is CVE-2026-123158. On an affected host, a person who can reach the SSH port can log in to an account that has no password, without a password. The cause, in one sentence: when sshd is built without PAM, an error value from musl's crypt() could be treated as a match for accounts that have no password.

This post tells you how to find out if a host or a container is affected, how to fix it, and what to look for in your logs. It does not show how to trigger the bug.

TLDR

  • You are affected only if four conditions are all true. In our tests, a fresh Alpine container with openssh-server failed two of them.
  • The fix is apk upgrade -U -a, then a restart of sshd. The upgrade alone does not replace the running daemon.
  • If you cannot upgrade, set PasswordAuthentication no and restart sshd.
  • Our read-only script checks the four conditions, the package version and the restart for a host or for every running container.

Prerequisites

  • Root on the Alpine host, or access to docker exec for containers
  • A second SSH session open before you change sshd settings

Who is affected

The Alpine advisory says you are affected only if all of these are true:

  1. sshd is built without PAM. The openssh-server package is. The openssh-server-pam package is affected too if UsePAM is no, which is the OpenSSH default when the config does not set it.
  2. PasswordAuthentication yes, which is the OpenSSH default.
  3. An account has * in its password field. On Alpine base images root is root:*, but the default PermitRootLogin prohibit-password blocks password logins for root. Key-only users, the alpine user on tiny-cloud images, and directory users through musl-nscd (they have no shadow entry) also count.
  4. At least one local account has a $5$ or $6$ hash. One user with a password is enough.

You are not affected if PasswordAuthentication is no, if you use openssh-server-pam with UsePAM yes, if no account has a $-style hash, or if your containers do not run sshd. Locked accounts (! in the password field) are not affected. Instances where cloud-init sets PasswordAuthentication no are not affected.

These are the fixed openssh versions from the advisory:

Branch Fixed version Branch Fixed version
edge 10.6_p2-r0 3.20 9.7_p1-r6
3.24 10.3_p1-r2 3.19 9.6_p1-r3
3.23 10.2_p1-r1 3.18 9.3_p2-r4
3.22 10.0_p1-r11 3.17 9.1_p1-r7
3.21 9.9_p2-r1 3.16 9.0_p1-r6

Check one host

We wrote a small POSIX sh script that reads these facts and changes nothing:

It reads the branch from /etc/alpine-release, the installed package version from apk, the effective settings from sshd -T, and whether the running sshd is still the sshd binary on disk. From /etc/shadow it reads only the type of each password field: it counts * fields and $5$/$6$ fields. It never prints a hash or a user name, and it does not connect to sshd. Because it runs as root, it executes only the packaged sshd binaries in /usr/sbin, and only if root owns them and no one else can write to them. If openssh can come from edge on a stable host (an untagged edge repository, or a pin such as openssh-server@edge), it compares against the edge fix, and it says "cannot tell" when it cannot find where the package came from. Exit codes are 0 not affected, 1 affected, 2 fixed but restart needed, 3 cannot tell. Add --json for machine-readable output.

sudo sh check.sh

We ran it in throwaway containers on Alpine 3.21, 3.22, 3.23, 3.24 and edge after apk add openssh-server. sshd ran with --network none, so no port was open to anyone. Every branch installed the fixed version, and every run exited 0. The run below is the closest one to an affected host: Alpine 3.24 with a key-only user set to * and one user with a random password, so all four conditions are true. The version is the only thing that keeps it safe:

Two things we found while we ran it:

  • On 3.24, adduser -D deploy wrote ! (locked), not *. A locked account is not affected, but it cannot log in with a key either. If you changed key-only users to * so that key login works, those are the accounts that matter.
  • OpenSSH 10.6 prints sshd -T keywords in mixed case (PasswordAuthentication yes). Older versions print lower case. The first version of our script matched only lower case and reported "unknown" on edge. Use grep -i in your own checks.

Check containers

Containers that do not run sshd are not affected. To find the ones that do, scan-containers.sh lists the running containers, skips those that are not Alpine or have no sshd process, and pipes check.sh into the rest with docker exec -i ... sh -s. Nothing is copied into the container. Because it runs each container's own sh as root inside that container, scan only containers whose images you trust.

If you build images that install openssh-server, a rebuild can reuse a cached apk add layer with the old package. Rebuild with docker build --pull --no-cache, then replace the running containers. A restart of the old container does not change its package.

Fix it, and restart sshd

apk upgrade -U -a
rc-service sshd restart

The restart is not optional. The package upgrade replaces the files on disk, but it does not restart the sshd that is already running, and the advisory says to restart it after the upgrade. check.sh checks whether the running sshd is still the same file as the sshd binary on disk. An upgrade writes a new file, so a daemon that was not restarted is a different file, and the script exits 2. Run it again after the restart.

On edge, the fix comes with OpenSSH 10.6p2. If you jump from an older 10.x, read our post on what OpenSSH 10.6 changed first: it refuses some usernames and changes ssh -C.

Workaround: turn off password logins

If you cannot upgrade now, turn off password authentication. Either step closes the hole.

Warning

Make sure that key login works first. Keep your current session open, and test a new key login in a second session before you close it.

mkdir -p /etc/ssh/sshd_config.d
echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00-no-passwords.conf
sshd -t && rc-service sshd restart
sshd -T | grep -i passwordauthentication   # must print: passwordauthentication no

Alpine's default sshd_config reads /etc/ssh/sshd_config.d/ from 3.19 onward. On 3.18 and older, change the line in /etc/ssh/sshd_config itself; the advisory has a sed command for that. A Match block can turn password logins back on for some users or addresses, and sshd -T without -C does not show that, so read your Match blocks too. In our 3.24 test container, the drop-in changed condition 2 to no.

The tiny-cloud alpine user

The official tiny-cloud images (qcow2, VHD and AMI) from 3.18.11 to 3.24.3 have an alpine user with * in the password field and passwordless doas. They are safe on first boot. They become exposed as soon as any $6$ hash is on the machine, for example when an admin sets a console password or user-data adds a user with hashed_passwd. Then an attacker can get a shell as alpine and use doas to become root.

Images from 3.21.10, 3.22.8, 3.23.8 and 3.24.4 contain the fixed package. Instances that started from older images do not get the fix from a new image: upgrade openssh on each one, or turn off password logins.

What to look for in logs

Look for password logins to accounts that have no password. On a host with syslog, that is /var/log/messages. For a container that runs sshd -e, it is docker logs.

# accounts with '*' in the password field (names only)
awk -F: '$2 == "*" { print $1 }' /etc/shadow

# password logins; compare the user names with the list above
grep 'sshd.*Accepted password for' /var/log/messages
docker logs <container> 2>&1 | grep 'Accepted password for'

Any Accepted password for alpine, for root, or for a key-only user is a bad sign. On exposed machines, also review doas use, new users, new files, and changes to authorized_keys files. If you cannot rule out such a login, treat the machine as compromised and rebuild it.

What we could not check

  • A vulnerable package or a real upgrade. Alpine replaced the packages on every branch the same day, so each fresh install got the fixed version. Exit codes 1 and 2 are simulations in the repo's test-exit-codes.sh: a copy of the script with a raised fixed version, and the same sshd binary renamed over /usr/sbin/sshd while sshd runs. Both gave the expected code.
  • openssh-server-pam, musl-nscd users, tiny-cloud images and branches 3.16 to 3.20. We did not measure them.
  • Other musl systems. The CVE record covers sshd before 10.6p2 on Linux with musl and without PAM, and names Alpine as the example. We did not check other musl-based distributions or firmware builds.

Sources: the Alpine advisory, the OpenSSH 10.6p2 release notes and the CVE record.

Run the commands from this article in the browser. Nothing to install.

Published: 2026-11-05|Last updated: 2026-11-05T21:00:00Z

Tags

SecurityLinuxOpenSSHSSHAlpine LinuxDockerCVE

Found an issue?

Also worth your time on this topic