Back to Blog
OpenSSHAlpine LinuxCybersecurityVulnerability ManagementLinuxContainers

OpenSSH CVE-2026-123158 (SSHole): Who Is Exposed on Alpine and How to Fix It

An OpenSSH flaw lets attackers log in to passwordless accounts on Alpine and other musl systems, and it is being exploited. Who is exposed and how to fix it.

P
Rafael Melo
November 12, 202611 min read

On 5 November 2026, OpenSSH released version 10.6p2 to fix CVE-2026-123158, a password authentication bypass in sshd that its finder called "SSHole". It only affects Linux systems where sshd is built against the musl C library and runs without PAM. Alpine Linux, the base of a large share of container images and a popular choice for small cloud servers, is exactly that. On an affected machine, an attacker who can reach the SSH port can log in to accounts that were never meant to have a password at all.

It did not stay theoretical for long. Exploit code was public the same day, attacks started within hours, and the vulnerability went on the official list of vulnerabilities known to be exploited on 9 November.

Most servers are not affected: Debian, Ubuntu, Red Hat and the other glibc distributions use a different library and are out of scope. The problem is that the machines that are affected are often the ones nobody thinks of as servers: a small Alpine VM, a bastion host, an SFTP container. Below: what the flaw is, exactly which machines are exposed, how to check, how to patch, what to do if you cannot patch today, and how to tell whether someone already got in.

What the Flaw Actually Is

Linux keeps password hashes in /etc/shadow. An account whose password field is just * has no password: no hash can ever produce *, so no password login can succeed. That is the standard way to create an account that logs in with an SSH key only. The obvious alternative, a password field starting with !, does not work for that, because sshd treats ! as a locked account and refuses it even with a valid key.

musl's crypt() function, which sshd uses to hash the password you type, returns the string * when it cannot hash its input. Its SHA-256 and SHA-512 implementations refuse passwords longer than 256 characters, so a long enough password makes crypt() return *. sshd then compared that result with the stored *, found them equal, and let the attacker in. OpenSSH accepts passwords up to 1,024 characters, which is why the official description says "any password between 256 and 1024 characters succeeds".

The OpenSSH team considers the root cause to be outside sshd, in how the C library reports the error, but sshd is where it could be stopped. Version 10.6p2 refuses to compare a password against a stored value that is too short to be a real hash. The bug is rated 8.1 out of 10; the OpenSSH release notes call the fix "a critical security workaround".

We reproduced it in a lab. On Alpine, OpenSSH 10.6p1 built without PAM let us log in to a * account with an over-long password. Version 10.6p2 refused the same attempt, and so did 10.6p1 with password authentication turned off.

Who Is Exposed

A machine is exposed when all of these are true:

  • sshd is linked against musl and runs without PAM. That is Alpine's default openssh-server package. The openssh-server-pam package is safe only when UsePAM yes is set; with UsePAM no it takes the same vulnerable path.
  • The OpenSSH package is older than the fix (versions are in Step 2).
  • Password authentication is enabled. It is OpenSSH's default, and Alpine does not change it.
  • At least one account has * as its password field.
  • At least one other account has a real SHA-256 or SHA-512 password hash ($5$ or $6$). sshd borrows that hash's settings when it checks a * account, and that is what sends the long password to the code that fails. Without such an account, the attempt fails. We confirmed this in the same lab.

The attacker needs nothing else: network access to the SSH port and the name of a * account. Root is out of reach directly, because OpenSSH's default refuses root password logins (PermitRootLogin prohibit-password), but any * account with passwordless doas or sudo is a path to root.

The worst case: Alpine cloud VMs

Alpine's cloud images set up the default user (normally called alpine) with a * password, add it to the wheel group, and give that group passwordless doas, and passwordless sudo when sudo is installed. We read this in the package that does it. Nothing in that setup turns password authentication off, so it stays at the default: on.

Such a VM is safe until one more thing happens: someone gives any account a real password. Setting a root password "just in case", or creating a second user with passwd, completes the conditions above. From then on, an attacker can log in as alpine and become root.

Containers

The official Alpine image on its own is not exposed: it has no sshd, and no account in it has a password hash. Images that add sshd are different. SFTP servers, bastion or jump-host images, and in-house "debug" images that run sshd and set a password for one account are the ones to check. Containers do not get fixed by patching the host: the vulnerable sshd is inside the image, so the image has to be rebuilt.

Some images use the * pattern correctly. Self-hosted Git servers on Alpine, Gitea's image for example, set the git account to * for key-only access but ship sshd with password authentication off. Unless you turned it back on, that default is not exposed.

Not exposed

  • Debian, Ubuntu, Red Hat Enterprise Linux, Rocky, Alma, Fedora, SUSE and other glibc distributions.
  • Alpine with openssh-server-pam and UsePAM yes.
  • Dropbear, the small SSH server used by many embedded systems, which is a different program.

Other systems built on musl, such as Void Linux's musl edition or firmware built with Buildroot or Yocto, are exposed only if they run OpenSSH's sshd without PAM. Check them with the same steps.

How Big This Is

  • Exploited within hours. The fix and a working exploit were public on 5 November. Researchers watching the internet saw attempts that day and the first successful break-ins on 6 and 7 November.
  • What attackers did once inside: look around the system, query the cloud metadata service, try to read password hashes and root's SSH keys, and add their own keys to authorized_keys. In at least one case they reached root and installed a remote-control agent.
  • A ten-year-old code path. Alpine's security tracker lists every openssh package back to 7.4, from late 2016, as possibly vulnerable, so old and forgotten machines are in scope too.

The honest caveat: the conditions are narrow. A machine needs a * account, another account with a password, password logins enabled and the vulnerable package all at once. Many Alpine boxes miss one of them. But the conditions are easy to meet by accident, the check takes a minute, and the fix is a package update.

Not sure which of your machines and images run sshd on Alpine? That is the first question. Request a free OpenSSH exposure assessment and our security team will map it with you.

Step 1: Check Whether a Machine Is Exposed

Run these as root on the machine, or inside the container. First, the C library and the installed sshd package:

bash
ldd --version 2>&1 | head -1
apk list -I 2>/dev/null | grep -E '^openssh-server'

musl libc on the first line means the machine is in scope. The second command prints the installed version, to compare with Step 2. If it shows openssh-server-pam, check that PAM is actually on.

Then the effective sshd settings, which include any drop-in files:

bash
sshd -T | grep -E '^(passwordauthentication|usepam|permitrootlogin) '

passwordauthentication yes keeps the machine in scope. A build without PAM does not print a usepam line at all.

Then the two account conditions:

bash
# accounts with no password: the ones an attacker can log in to
awk -F: '$2 == "*" {print $1}' /etc/shadow
# accounts with a real SHA-256 or SHA-512 password hash
awk -F: '$2 ~ /^\$[56]\$/ {print $1}' /etc/shadow

If both lists are non-empty, password authentication is on and the package is older than the fix, the machine is exposed. Finally, check what a * account could do after logging in:

bash
grep -hs nopass /etc/doas.conf /etc/doas.d/*.conf
grep -rhs NOPASSWD /etc/sudoers /etc/sudoers.d/

A line like permit nopass :wheel means any member of wheel becomes root without a password.

For container images, check what is inside them, not what runs on the host. This loop takes the images running in a Kubernetes cluster and prints the ones that contain sshd, with its version:

bash
for img in $(kubectl get pods -A -o jsonpath='{..image}' | tr ' ' '\n' | sort -u); do
  docker run --rm --entrypoint sh "$img" -c 'apk list -I 2>/dev/null | grep -E "^openssh-server(-pam)?-[0-9]"' 2>/dev/null && echo "  ^ $img"
done

Images without a shell or without apk print nothing; they are not Alpine images running sshd.

Step 2: Patch and Restart sshd

Alpine published fixed packages on 5 November, including for branches past their end of support. The fixed openssh versions:

  • 3.24: 10.3_p1-r2
  • 3.23: 10.2_p1-r1
  • 3.22: 10.0_p1-r11
  • 3.21: 9.9_p2-r1
  • edge: 10.6_p2-r0

On a VM or bare-metal Alpine host:

bash
apk update
apk upgrade
rc-service sshd restart
apk list -I 2>/dev/null | grep -E '^openssh-server'

The restart matters. Upgrading the package does not replace the sshd that is already running, and until it restarts the old code keeps answering. Keep your current session open while you restart, in case something in the configuration was already broken.

For containers, patching a running container is lost the next time it starts. Rebuild the image on an updated base so the new build installs the fixed package, then redeploy:

bash
docker build --pull --no-cache -t IMAGE:TAG .

--pull fetches the current base image and --no-cache stops Docker from reusing the old apk add openssh layer. If your images come from a CI pipeline, rebuild there and roll the workloads out. Our DevSecOps practice can add a gate that blocks images with known exploited vulnerabilities from reaching the cluster.

Step 3: If You Cannot Patch Today, Turn Off Password Logins

The bypass only works through password authentication. Turning it off closes it completely, whatever the package version. We tested that too: the vulnerable 10.6p1 refused the attack once password authentication was off.

First make sure you can log in with a key, because this locks out anyone who only has a password. Then create a drop-in file:

bash
printf 'PasswordAuthentication no\nKbdInteractiveAuthentication no\n' > /etc/ssh/sshd_config.d/00-no-passwords.conf
sshd -t && rc-service sshd restart
sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication) '

The last command must print no twice. sshd uses the first value it reads for each setting, and Alpine's configuration reads the drop-in directory near the top of /etc/ssh/sshd_config, so this file normally wins. If it still prints yes, your configuration sets the value before that point, or it is an older release without the drop-in directory. In that case, put the two lines at the very top of /etc/ssh/sshd_config and check again.

This is also the right long-term setting for almost any server reachable from the internet: keys or certificates for people, no passwords at all.

Step 4: Check Whether Someone Already Got In

Exploitation started on 5 November. If an exposed machine was reachable from the internet after that date, look before you assume it is clean.

Successful password logins to accounts that have no password should not exist. On a default Alpine install, sshd's messages go to the system log in /var/log/messages:

bash
grep 'Accepted password for' /var/log/messages

Compare every account in the output with your * list from Step 1. In our lab, the bypass showed up as an ordinary Accepted password for line, right after a Failed password line for the same account. Any Accepted password for a * account is a break-in.

Then look for what the attackers were seen doing:

bash
# authorized_keys files changed since the fix came out
touch -d '2026-11-05 00:00' /tmp/since
find /root /home -name authorized_keys -newer /tmp/since
# services and scheduled jobs you do not recognise
rc-status -a
ls -la /etc/periodic/*/ /etc/crontabs/

If you find evidence, treat the machine as compromised. Patching closes the door but does not remove keys, users or programs an attacker left behind. Rebuild it from a clean image, and rotate everything it held or could reach: SSH keys, API tokens, database passwords, and the cloud credentials and user data the metadata service would have handed out.

Running Alpine alongside other Linux systems? Kernel flaws need the same discipline: see how we handled Linux kernel CVE-2026-98432, and our Linux consulting practice keeps fleets patched without the late-night scramble.

Free OpenSSH Exposure Assessment

If you are not sure where Alpine and sshd sit in your estate, we will check it with you:

  • Inventory: every VM, bare-metal host and container image that runs sshd on musl, with its package version.
  • Exposure: password authentication, * accounts, password hashes and passwordless doas or sudo, per machine.
  • Compromise check: login history, authorized_keys changes and persistence on every machine that was exposed after 5 November.

You get a written list of exposed machines and the exact fix for each, with no obligation attached.

Request your free assessment, or see how our cybersecurity team works.