Skip to main content
Resources

CVE-2026-123158 (*sshole): the SSH bug that logs in with no password

CVE-2026-123158 (*sshole): the SSH bug that logs in with no password

Some accounts on a Linux server have no password at all. They are meant to be reached with an SSH key, or not logged into directly, with password login switched off entirely. CVE-2026-123158 lets an attacker log into one of those accounts over the network by sending a single very long password, a few hundred random characters, with no key and no real password involved. It was published on 5 November 2026. Özgün Kültekin of Wakefield found it by running about 17,600 automated login attempts with AI agents, named it *sshole and wrote it up in detail.

It sounds worse than it is for most people, because it only works on one specific kind of server. The SSH service has to be built against musl, a lightweight version of the core C library that only a handful of Linux distributions use, Alpine Linux being the main one, and it has to check passwords itself instead of handing them to the system login service, PAM. Outside of Alpine, almost nothing is set up that way, and OpenSSH 10.6p2 shipped the same day to close the hole. You will also see the bug written sshole, sshhole, SSHhole or SSHHole; they are all the same thing.

For a Kubernetes cluster, the question is which of your nodes run that combination. Nodes based on Ubuntu, Debian, RHEL, SUSE, Fedora, Flatcar or Bottlerocket do not, so they need no action. The ones to check are Alpine-based nodes, along with any Alpine-based container image that runs its own SSH server with password logins enabled.

Note

Syself Autopilot is not affected by CVE-2026-123158 (*sshole), on any release. Every node generation uses glibc and checks passwords through PAM, so the bypass has nothing to act on.

For extra precaution, you can lock down node SSH, narrowing it to a bastion host or disabling it at the host firewall: see Restrict SSH access. The Version Hub tracks the current status of this CVE and others, per release.

What musl, glibc and PAM are 

A C library is the code a program leans on for ordinary work: opening files, talking to the network, and turning a typed password into a hash through a function called crypt(). Nearly every Linux program uses one without ever naming it. glibc is the long-standing one, shipped by Ubuntu, Debian, RHEL, SUSE and Fedora. musl is a second implementation of the same standard, written to be small and simple, and Alpine Linux is built on it, which is much of why Alpine images are so tiny. The same sshd can be linked against either, and it borrows that library's crypt(). This bug lives in a spot where the two behave differently, so which libc a node uses is what tips it from safe to exposed.

PAM, short for Pluggable Authentication Modules, is a separate framework that decides whether a login is allowed. It is why a company can add two-factor login to every SSH server by adding a PAM module, without touching sshd. When sshd is built with PAM, it hands the password over and lets PAM rule on it. When it is not, sshd does the check itself: it calls crypt() and compares the result against the account's stored field. The bypass is in that second path. A node with PAM in the login path never reaches the comparison that misfires, so PAM keeps the bug out whatever the libc underneath.

What CVE-2026-123158 actually is 

Plenty of accounts on a Linux machine are never meant to take a password: background services, the default alpine user on Alpine's cloud images, a deploy user that only ever logs in with a key. Since 1980, Unix has marked such accounts with a single asterisk, *, written where the password hash would normally sit. A hash always has a fixed format and * is not one, so no password can ever match it.

The bypass comes from three safeguards, each added for a good reason, meeting on a login to one of those accounts:

  1. sshd borrows another account's salt. To check a password without PAM, sshd hashes what was typed using the settings stored with the account, the hash type and the salt, and compares the result with the stored value. A * has no settings to use, so sshd borrows them from the first account on the system that has a real hash. It does this on purpose: without a hash to compute, the check would fail instantly, and an attacker timing the replies could tell real accounts from missing ones. That leak was CVE-2016-6210, fixed in OpenSSH 7.3 in 2016.
  2. musl refuses passwords over 256 bytes. For SHA-256 and SHA-512 hashes, hashing time grows quickly with password length, so musl caps the input to keep a single login from tying up a CPU core. Past the cap, crypt() fails.
  3. musl reports that failure as *. The standard lets crypt() return NULL on failure, and programs that never checked for it crashed. In 2012 musl switched to returning *, a value no real hash can equal. It even guards against the obvious collision: when the salt it is given is itself *, it returns x instead. But sshd passed in the borrowed salt, not the *, so that guard never fires.

Put together, on an affected system:

text
				/etc/shadow
  alpine:*:...              ← "*": no password should ever match
  bob:$6$Qx3f…$Tz9…:...     ← an ordinary account with a real hash
 
ssh alpine@host, password = "A" × 300
  1. "*" has no salt, so sshd borrows bob's: "$6$Qx3f…"
  2. crypt("AAA…", "$6$Qx3f…") → over 256 bytes, musl fails and returns "*"
  3. "*" == "*" → logged in
			

Any password from 257 to 1,024 bytes works, per the Alpine advisory.

Tip

What protects you is the server setting, not how your users log in. With PasswordAuthentication no, sshd never checks a password, so nothing is exposed on any libc. A user who only ever logs in with a key is a different matter: if the server still accepts passwords, that user's * account is exactly what the bypass targets.

All of the following have to be true at the same time, per the Alpine advisory:

  1. sshd is linked against musl libc on Linux.
  2. sshd runs without PAM, either built without it (Alpine's openssh-server package) or with UsePAM no.
  3. PasswordAuthentication is yes, which is OpenSSH's default.
  4. The target account has * as its password field. An account locked with ! is safe.
  5. At least one other local account has a $5$ or $6$ hash, as the salt source.

On Alpine, the root account itself is a * account, but the default configuration blocks password logins to root, so root is not the entry point. The account Alpine warns about is a second * account with a way to escalate, such as the alpine user in some of its cloud images, which can reach root once an attacker is logged in as that user.

OpenSSH shipped 10.6p2 the same day. It refuses to compare a password against a stored value shorter than four characters, so a * can never match again. The OpenSSH team calls it a workaround and puts the root cause in musl. musl's maintainer calls it a caller error, though he expects musl to change the behaviour in its next release cycle. For anyone running an affected system, the answer is the same either way: upgrade OpenSSH.

How serious is it? 

The trackers do not agree on a number. MITRE, which assigned the CVE, scores it 8.1 out of 10, high. The commercial database VulDB puts it at 10.0, critical. They are describing the same result, a full login bypass, and they split on a single input to the formula: attack complexity. MITRE marks the complexity high because the exploit only fires when those five conditions line up on one machine, and that pulls the number down from a theoretical ten. VulDB scored the raw impact and set the preconditions aside.

In practice the risk is close to binary. Where every precondition is met, the impact is at the top of the scale: unauthenticated remote access to an account that was never meant to accept a password, a complete authentication bypass. Where any one precondition is missing, the flaw cannot be reached at all. That split is why a single base score fits it poorly; the figure that should drive your response is the environmental score you assign after checking your own hosts, not the published headline number.

Exploitation is still theoretical. A public proof of concept exists, there are no reports of use in the wild, the CVE is not in CISA's Known Exploited Vulnerabilities catalog, and EPSS has not scored it yet. What it does have is reach: the security press covered it within hours, BleepingComputer and The Hacker News among them, which is worth keeping in mind when a colleague forwards the headline and asks whether you need to act.

Which systems are affected 

Run through your node images and any container that ships an SSH server. Almost everything in a normal Kubernetes cluster lands on no.

What the machine runsAffected?
Ubuntu, Debian, RHEL, SUSE, Fedora, Flatcar or Bottlerocket (all glibc)No
Alpine, with SSH password login turned onYes, until you upgrade OpenSSH
Alpine, key-only login (the usual default)No
A container image with no SSH server, which is most of themNo
A container built on Alpine running sshd with passwords onYes, until you rebuild it
macOS, the BSDs, or anything that is not LinuxNo

Alpine shipped fixed OpenSSH packages for every supported branch on 5 November 2026; the Alpine advisory lists the exact version for each one. A handful of other musl systems, like a Void musl install or a custom Buildroot image, can be caught too, but only under the same three conditions: OpenSSH, no PAM, and password login left on.

Does this reach your Kubernetes cluster? 

A cluster has three places where an sshd can run.

The node operating system. Almost every Kubernetes node image is glibc: Ubuntu and Debian cloud images, the RHEL family, Flatcar, Bottlerocket. Alpine is the exception, so the nodes worth checking are the Alpine ones: a k3s or k0s cluster on Alpine VMs, an edge device, a home lab, or a cloud image built from Alpine's tiny-cloud. Those are affected until their openssh package is upgraded, and until then setting PasswordAuthentication no closes the issue without an upgrade. The concern grows once a node's SSH port is reachable from the open internet, where anyone can connect and attempt a login. That exposure is worth weighing on its own when you decide whether to put Kubernetes nodes on the public internet.

Container images that run sshd. The official Alpine image ships no SSH server, and the advisory says in plain words that containers which do not run sshd are not affected, so the Alpine tag on an image tells you nothing by itself. The pods that matter are the ones somebody built on Alpine with an SSH daemon inside: SFTP servers, git servers, bastion or jump pods, CI runners with SSH access, developer workspaces. Most of those images turn password logins off, because key-only is the usual default in that space. The ones that leave PasswordAuthentication yes and expose the port through a Service or hostPort are affected until rebuilt on a fixed openssh package.

The control plane. On a cluster you run yourself the control plane nodes are nodes like the others, so the node OS answer applies. On a managed service the provider owns the control plane hosts, and you cannot check them. Ask for the base image and libc.

On Syself Autopilot, every node generation runs glibc and checks passwords through PAM, so the bug is ruled out across the board. Releases 1.34 and 1.35 run stock Ubuntu 24.04 with UsePAM yes; release 1.36 runs Syself Linux with glibc 2.43 and linux-pam 1.7.3, and keeps password logins off as well.

Nodes do run sshd on port 100, for emergency access. If you would rather not expose it, both options are documented in Restrict SSH access:

  • Narrow it to a bastion host, so only one address can reach port 100.
  • Turn it off at the host firewall, closing node SSH entirely.

For the CVE itself, every release gets a daily rescan on the Version Hub and a published verdict with the reasoning behind it, the same for this bug as any other.

How to check whether you're exposed 

Every command below reads state. Run them on a node, or inside a container that runs sshd, as root.

Which C library sshd uses:

sh
				ldd "$(command -v sshd)" | grep -i musl
			

No output means glibc or another libc, and you can stop here.

Whether PAM is in the path and password logins are on:

sh
				sshd -T | grep -Ei '^(usepam|passwordauthentication|permitrootlogin)'
			

usepam no together with passwordauthentication yes is the exposed combination. On Alpine, the plain openssh-server package reports usepam no because the binary has no PAM support.

Which accounts carry the * marker and which carry a real hash:

sh
				awk -F: '$2 == "*" {print "star:", $1}' /etc/shadow
awk -F: '$2 ~ /^\$(5|6)\$/ {print "hash:", $1}' /etc/shadow
			

At least one line of each kind is what the bug needs.

Users that come from a directory service such as LDAP, through musl-nscd, have no line in /etc/shadow, so these commands don't list them, and the Alpine advisory counts them as affected. If your hosts use one, check PasswordAuthentication instead: no rules them out too.

Which openssh package is installed, on Alpine:

sh
				apk version openssh-server
			

Compare it against the fixed version for your branch in the Alpine advisory, then upgrade with apk upgrade.

Warning

The package upgrade does not restart a running sshd. The old, vulnerable code stays in memory until you restart the daemon yourself.

sh
				rc-service sshd restart
			

Across a cluster, list the images in use and look for the ones that ship an SSH daemon:

sh
				kubectl get pods -A -o jsonpath='{range .items[*]}{range .spec.containers[*]}{.image}{"\n"}{end}{end}' | sort -u
			

Finally, check whether a * account ever logged in with a password. It should never happen, so a single hit is worth a look:

sh
				grep 'Accepted password for' /var/log/messages
journalctl --grep 'Accepted password for'
			

Compare the user names against the star: list from above.

Your base image and node OS are a security policy 

It would be easy to read this week as a point against musl. musl has a shorter CVE history than glibc, and glibc had its own privilege-escalation disaster in Looney Tunables (CVE-2023-4911). Neither library is the safe choice by nature. The bug took four decisions, made by different projects over 46 years, each sensible on its own:

YearDecisionWhy
1980Unix marks accounts that take no password with *Nothing hashes to *, so no password matches
2012musl's crypt() returns * instead of NULL on failurePrograms that never checked for NULL crashed
2016OpenSSH hashes failed logins with another account's saltResponse times stopped revealing which accounts exist
2022Alpine 3.16 sets root's password field to * instead of !Without PAM, sshd treats ! as locked and refuses SSH keys too

The hole is in the seam between them, and no single project owned the seam. On systems that meet the conditions, it has been open since 2016.

That is what a base image is: a bundle of defaults someone else chose, plus the assumption that they fit together. Picking Alpine for size or a distroless image for a small attack surface, over Debian or Ubuntu, is a security decision as much as a build decision, and it needs an owner: someone who knows which defaults came with it and watches the advisories for every part.

The same is true one level down, for the node OS, where the bundle includes the kernel, the libc, the init system and the login daemon. Nobody on the Kubernetes side owns that bundle for you. A node image is a release of its own and deserves the same treatment: a known list of parts, a test of the whole, a rescan when something new comes out, and a written verdict you can show an auditor. That is how we build and ship the node OS, and it is why this CVE, like the kernel TCP bug last month, ends as a line on the Version Hub.

CVE-2026-123158 FAQ 

Is my Kubernetes cluster affected by CVE-2026-123158? 

Almost certainly not. The mainstream Kubernetes node operating systems, Ubuntu, Debian, Flatcar, Bottlerocket and the RHEL family, all use glibc, and the bug only affects musl. The two things actually worth checking are Alpine-based nodes, which are uncommon, and Alpine-based container images that run their own SSH server with password logins enabled. Everything else is clear.

If our SSH is key-only, are we affected at all? 

No. The whole bug lives in the password check, and an SSH server that accepts keys only never runs that code. If you have PasswordAuthentication no across your fleet, then whatever the libc underneath, there is nothing further to check.

Our nodes run Ubuntu, Debian, Flatcar or Bottlerocket. Anything to do? 

No. Those are all glibc, and the bug is specific to musl's version of the password-scrambling function. The OpenSSH developers put it the same way: glibc systems, and systems that authenticate through PAM, are not affected. RHEL, SUSE and Fedora sit in the same clear.

We ship a few Alpine-based containers, like SFTP or CI runners. Do those count? 

Only if one of them actually runs an SSH server with password logins on. The base Alpine image has no sshd, and a container that runs no sshd is not affected, so the Alpine tag by itself tells you nothing. The ones to check are images somebody built with openssh-server and left PasswordAuthentication yes on. Rebuild those on a fixed openssh package.

Is Alpine itself affected, and is there a fix? 

Yes, on any Alpine system with password logins enabled. Alpine shipped fixed packages for edge and every branch from 3.16 up on 5 November 2026, with the exact versions in the Alpine advisory. Upgrade with apk upgrade -U -a, then restart the daemon, because the running sshd keeps the old code until you do: rc-service sshd restart.

Can an attacker log in as root? 

Not with OpenSSH's defaults. Root on Alpine is a * account, but PermitRootLogin defaults to prohibit-password, which refuses every password login to root whatever the password check returns. Logging in as root this way needs PermitRootLogin yes. The realistic path goes through another * account, such as the alpine user on Alpine's official cloud images, which can become root through doas without a password.

musl is the small, supposedly safer libc. How did it open a hole like this? 

By returning the wrong value in an edge case. Given a password over 256 bytes, musl's crypt() returns * instead of signalling an error, and * is exactly the marker for a disabled account. musl does carry a shorter CVE history than glibc, but this is a useful reminder that fewer CVEs is not the same as safe, and that the libc underneath your SSH server is something someone needs to account for.

Is anyone actually exploiting it? 

Not that anyone has reported. A proof of concept is public, but the CVE is not in CISA's KEV catalog and has no EPSS score yet. If you are running Alpine with password logins enabled, patch regardless, because the preconditions are common enough on those systems to be worth closing promptly.

What should I ask a managed Kubernetes provider? 

Four things: what the node base image is and which libc it uses, whether password SSH is turned off, whether PAM is in the authentication path, and how they get from a CVE like this to a yes-or-no answer per release. kubectl get nodes -o wide shows you the node OS image to start with, but the control-plane hosts you cannot inspect yourself, so the rest is a question for the provider. On Syself Autopilot the answers are glibc, password SSH off, PAM in the path, and a published verdict per release.

Ready to Build? Start Your Free Trial

Start with a 14-day free trial, and our step-by-step guides will walk you through your first cluster deployment in minutes.

Tags

SecurityKubernetes