sshole: The OpenSSH Bug That Logs You In to Accounts With No Password

On 5 November 2026, OpenSSH released Portable OpenSSH 10.6p2 to fix CVE-2026-123158, an authentication bypass nicknamed sshole by its finder, Özgün Kültekin of Wakefield. The CVE record scores it 8.1 high under CVSS 3.1 and classifies it as CWE-394, an unexpected status code or return value. Threat intelligence firm Previdian told Help Net Security it saw the first exploitation attempt less than nine hours after publication and confirmed root access on an Alpine Linux honeypot.

The behaviour is simple to state and uncomfortable to read. On an affected system, an attacker who can reach the SSH port can log in to an account that has no password, by sending a password that is deliberately too long. No stolen credential, no key, no brute force.

This advisory covers how three reasonable engineering decisions produced that outcome, the exact conditions required, why it lands hardest on container estates rather than on servers, and a detection with close to no false positives that you can deploy before the rollout finishes.

Three Decisions, Fourteen Years Apart

CVE-2026-123158 is a logic bug rather than memory corruption, and it sits in the gap between projects rather than inside any one of them. Several independent write-ups reconstruct the same chain.

  1. 2012, musl libc: POSIX says crypt() should return a null pointer when hashing fails. Too many callers never checked the return value and crashed, so musl chose to return the literal string * instead. A defensive decision, and a defensible one in isolation.
  2. The same library caps key length: musl’s SHA-based crypt refuses any password longer than 256 bytes, as protection against wasting CPU on absurd inputs. Past that limit, it fails, and failure now means returning *.
  3. 2016, OpenSSH: to fix CVE-2016-6210, a timing side channel that let attackers enumerate valid usernames by measuring whether sshd bothered to hash anything, OpenSSH 7.3 added salt borrowing. For an account with no real hash, sshd now takes a $5$ or $6$ salt from another local account and hashes the submitted password anyway, so that valid and invalid usernames take the same time. Also a sound fix for a real problem.
  4. Long-standing Unix convention: a single * in the password field of /etc/shadow marks an account that must never authenticate by password. Key-only accounts, service accounts, LDAP-mapped users and root on Alpine commonly carry it.

Put those together. An attacker offers a 300-byte password for an account whose shadow field is a single asterisk. Because of the 2016 change, sshd does not short-circuit, it borrows a salt from another account and calls crypt(). Because of the 2012 change and the 256-byte cap, musl returns the string asterisk. sshd compares that return value against the stored field, finds two identical asterisks, and treats the comparison as a successful authentication.

attacker password   : “AAAA…” (257 to 1024 bytes)

stored shadow field : *

sshd borrows salt   : $6$<salt from another account>$

musl crypt() returns: *          <- failure token, not a hash

sshd compares       : * == *     <- match, login accepted

 

The upper bound exists because sshd discards passwords over 1024 bytes, which is why the working range is roughly 257 to 1024. The fix in 10.6p2 is correspondingly narrow. sshd now rejects any stored hash shorter than four characters, whatever crypt() hands back, as IONIX’s analysis sets out.

Who Is Exposed, Precisely

This one has unusually tight preconditions, which is good news if you check them and bad news if you assume them. Every condition below has to hold at once.

  • sshd is linked against musl libc: Alpine Linux is the common case, and OpenWrt, Buildroot and Yocto builds are also named in reporting.
  • PAM is not in use: with PAM handling authentication, sshd never performs the crypt() comparison that fails open. Alpine’s default OpenSSH server configuration does not use PAM.
  • Password authentication is enabled: this is the OpenSSH default, so it holds unless someone deliberately turned it off.
  • A target account has a bare asterisk in its shadow field: the account the attacker wants, commonly root or a service account.
  • Another local account has a real $5$ or $6$ hash: the salt has to be borrowed from somewhere, so a system where no account has a usable password hash does not present the condition.

Equally important is what is not affected. Systems built on glibc, meaning Debian, Ubuntu and RHEL and their derivatives, return a null pointer or the token *0 on failure, neither of which can equal a stored asterisk, and sshd already rejects those. Systems using PAM are not in scope. Dropbear is reported as unaffected. If your estate is entirely glibc-based and PAM-authenticated, this advisory is an inventory exercise rather than an incident.

Why This Is a Container Problem

Reading the conditions above as a server problem will understate the exposure badly. Alpine is the default small base image across most container ecosystems, chosen precisely because it is minimal, and minimal is the reason PAM is absent.

Three consequences follow, and each one breaks a different assumption.

  1. The count is higher than your server inventory suggests: an organisation with no Alpine servers can still run thousands of Alpine containers. The question is not how many hosts run musl, it is how many images do.
  2. Most of those containers should not be running sshd at all: and many do, inherited from a base image or added for debugging and never removed. An sshd in a container that nobody remembers enabling is both the exposure and the finding.
  3. The fix travels by rebuild, not by upgrade: patching the host does nothing for an image built before 5 November. Alpine published fixed openssh packages across its supported branches, so the action is to rebuild on a patched base and redeploy, which is a different workflow from a package upgrade and a slower one.

The practical exposure question for a platform team is therefore narrow and answerable. Which running containers have an sshd listening, and of those, which are musl-based and reachable from anywhere an attacker sits. In most estates the honest answer to the first part is not known without checking.

# containers with a listening SSH port

kubectl get pods -A -o json | jq -r ‘.items[] | select(.spec.containers[].ports[]?

  | .containerPort == 22) | “\(.metadata.namespace)/\(.metadata.name)”‘

# on a host, confirm the libc and whether PAM is compiled in

ldd /usr/sbin/sshd | grep -E ‘musl|libc’

sshd -T 2>/dev/null | grep -E ‘usepam|passwordauthentication’

A Detection With Almost No False Positives

Memory-corruption bugs are hard to detect because failure looks like instability. This one is the opposite, and the SOC should take the gift. Successful exploitation produces a specific, logged event that has no legitimate counterpart. A password authentication succeeds for an account whose password field says it can never accept one.

The detection is a join between an authentication event and account state.

rule: ssh_password_login_to_locked_account

  source: sshd auth log

  match:  “Accepted password for <user>”

  where:  <user> shadow field is “*” or “!” at the time of login

  and:    host libc is musl

  severity: critical, no tuning expected

 

Supporting signals worth collecting alongside it, each weaker on its own and useful for confirming a case.

  1. Accepted password for root: on any Alpine host or container. Root on Alpine commonly carries the asterisk field, so this is close to decisive in that environment.
  2. Authentication on a container that should have no interactive access: any successful SSH login to a workload container is worth an alert on its own merits, independent of this CVE.
  3. Unusually large SSH authentication requests: the attack requires a password of at least 257 bytes. Packet sizes during authentication are not something most teams record, but where network telemetry allows it, an anomalous request size at the auth stage is a direct indicator of an attempt rather than a success.
  4. Repeat attempts across accounts: an operator will try root and then service accounts in sequence from the same source, which forms the usual spray pattern and gives a strong correlation key.

Mapped to ATT&CK, a successful bypass sits in Valid Accounts via Exploitation of Remote Services, and what follows is whatever the operator does with root inside a container that likely holds deployment credentials and application secrets. That second half is the real impact, and it is where the correlation matters more than the individual alert.

What the SOC Should Do This Week

    1. Hunt retrospectively before anything else: exploitation was reported within nine hours of publication, so the question is whether it already happened. Run the detection above against stored authentication logs back to 5 November rather than only forward from today.
    2. Disable password authentication where it is not needed: setting PasswordAuthentication no removes the precondition entirely and is a reversible configuration change rather than a rebuild. For container images that should never accept interactive logins, removing sshd is better still.
    3. Rebuild and redeploy Alpine-based images: on patched packages, treating this as a distinct workstream from host patching, with its own completion tracking.
    4. Check reachability rather than assuming it: a vulnerable container that no attacker can reach is a different priority from one behind a load balancer. Confirm which SSH ports are exposed outside the cluster rather than inferring it from network policy intent.

    On the response side, the governance line sits where it usually does. Blocking a source address and disabling password authentication on a service are reversible and can release under policy. Isolating a production workload or disabling an account that services depend on interrupts work and should reach a person with the evidence attached, which is the general position we set out in our post on governed autonomy.

Two Open Points, Stated Honestly

Reporting diverges on exploitation. Previdian’s observation of attempts within nine hours and root on a honeypot is specific and credited. Other vendor write-ups published on 6 November describe no confirmed in-the-wild exploitation, and at the time of writing the flaw is not listed in CISA’s Known Exploited Vulnerabilities catalog. Both statements can be true at once, since honeypot compromise and confirmed victim organisations are different evidentiary bars. Treat it as being exploited and verify the KEV position at publication.

Ownership of the defect is disputed. OpenSSH shipped the fix, and musl’s maintainer has described the problem as incorrect use of crypt() by the caller rather than a musl defect, while indicating the error behaviour is likely to change in a future release. That disagreement does not change what you have to do, and it is a useful reminder that a defect living between two correctly behaving components is nobody’s to own until someone is compromised by it.

The Record a Regulator Will Want

For an institution under SAMA’s Cyber Security Framework or the UAE’s Information Assurance Regulation, an authentication bypass with public exploitation reporting is the kind of event an examiner returns to. The expectation is evidence that the control operated, not proof that you patched within an hour.

  • The scoping decision: which systems were ruled in and out, and the technical basis for ruling them out, meaning glibc or PAM rather than an assumption.
  • The retrospective hunt: that you looked back to the disclosure date, what you searched, and what you found.
  • The interim control: password authentication disabled, who approved it, and when.
  • Closure per image, not per host: container rebuilds tracked separately, since that is where this one will quietly stay open.

Frequently Asked Questions about AI SOC Cost

What is the sshole vulnerability?

CVE-2026-123158 is an authentication bypass in OpenSSH sshd on Linux systems that use musl libc without PAM. A password between roughly 257 and 1024 bytes causes musl’s crypt() to fail and return the string asterisk, which matches the asterisk stored for accounts that have no password, so sshd accepts the login. It was fixed in Portable OpenSSH 10.6p2 on 5 November 2026.

Are Ubuntu, Debian or RHEL servers affected?

No. Those distributions use glibc, whose crypt() returns a null pointer or the token *0 on failure, and sshd already rejects both. Systems that authenticate through PAM are also out of scope, because sshd never performs the failing comparison.

Why does this matter more for containers than for servers?

Alpine Linux is the standard minimal container base image, and it is minimal precisely because it omits PAM. An organisation with no Alpine servers can still run large numbers of Alpine containers, some with an sshd inherited from a base image. The fix also arrives by rebuilding and redeploying images rather than by upgrading a package on a host.

What is the fastest mitigation if we cannot rebuild immediately?

Disable password authentication with PasswordAuthentication no, which removes a required precondition and is a reversible configuration change. For images that should never accept interactive access, removing sshd entirely is the stronger move. Both are compensating controls that belong alongside a rebuild plan with a date on it.

How do we detect whether this was already used against us?

Search stored authentication logs from 5 November 2026 onward for successful password logins to accounts whose shadow field is an asterisk, with root on Alpine hosts as the highest-value case. Unlike memory-corruption bugs, successful exploitation here produces a clean logged event with no legitimate equivalent.

Ready to get started?

Contact us to arrange a half day
Managed SOC and XDR workshop in Dubai

Ready to get started?

Contact us to arrange a half day Managed SOC and XDR workshop in Dubai

© 2026 HawkEye – Managed CSOC and XDR powered by DTS Solution. All Rights Reserved.
This is a staging environment