High
8.1
AV:N / AC:H / PR:N / UI:N / S:U / C:H / I:H / A:H
⚡ Exploited in the Wild

Summary

SSHole is an authentication bypass in OpenSSH's sshd before 10.6p2 on Linux systems using musl libc without PAM. Sending a password between 257–1024 bytes causes musl's crypt() to return * — the same sentinel value stored for accounts with no password — causing sshd to accept the login. Affects Alpine Linux in its default configuration. Active exploitation confirmed on a honeypot less than 9 hours after disclosure.

Affected Asset

OpenSSH sshd before 10.6p2, compiled against musl libc without PAM — primarily Alpine Linux (all branches before the Nov 5 2026 patches), OpenWrt, Buildroot, Yocto, and Alpine-based Docker images. glibc distros (Debian, Ubuntu, RHEL, Fedora) are not affected.

Affected Endpoint

OpenSSH sshd password authentication path — specifically the interaction between auth-passwd.c's call to crypt() and musl libc's crypt_sha512.c overlong-key handling.

Prerequisites

  • sshd linked against musl libc (Alpine, OpenWrt, Buildroot, Yocto, or Alpine Docker base images)
  • OpenSSH < 10.6p2 — unpatched openssh-server
  • PAM disabled — Alpine's default openssh-server package ships without PAM; also any configuration with UsePAM no
  • Password authentication enabled (PasswordAuthentication yes is the default)
  • At least one account with * as the shadow password field (key-only users, root on Alpine ≥ 3.16, default alpine user on AWS/Azure cloud images)
  • At least one other account with a real password hash (needed for sshd's fake-salt timing path to reference)

Steps to Reproduce

  • Identify a target Alpine host with password authentication open to the network
  • Target an account that uses * as its shadow entry — commonly root on Alpine ≥ 3.16, or any key-only deploy user
  • Send an SSH login with a password between 257 and 1024 bytes (any content, any printable characters)
  • musl's crypt() returns * due to the 256-byte cap; sshd compares * with the stored * — they match — login succeeds
  • On Alpine's default AWS/Azure images the alpine user has passwordless doas, so this yields an effective root shell
# Minimal proof of concept (Python)
import subprocess, "A" * 300
password = "A" * 300    # any payload > 256 bytes works
subprocess.run(["ssh", "-o", "PasswordAuthentication=yes",
                "root@<alpine-host>"], input=password.encode())

Evidence

  • Disclosed 5 November 2026 by researcher Özgün Kültekin (Wakefield) at sshhole.com
  • MITRE CVE-2026-123158 published same day; CVSS 3.1 score 8.1
  • Threat intelligence firm Previdian confirmed root access on an Alpine honeypot within 9 hours of disclosure
  • Public PoC available at github.com/oz9un/sshole
  • CISA rates exploitation as proof-of-concept; not yet in KEV catalog as of post date

Impact

An unauthenticated attacker with network access to SSH can log into any account whose /etc/shadow entry is * — without credentials, without keys, without prior access. On Alpine's official cloud images this is effectively unauthenticated root via the default alpine user. Any service, container, or VPS built on an Alpine base image running the default openssh-server package is in scope.

Root Cause — How the Bug Works

Three independent design decisions from across a decade of different projects collide to create this bug:

1980 – Unix tradition

Accounts that should never accept passwords store * in their password field. No real hash can produce *, so it is a safe sentinel.

2012 – musl libc

musl changes crypt() so that on failure it returns * instead of NULL (to avoid crashes in programs that don't check for NULL). Three weeks later, musl's SHA-512 crypt implementation caps passwords at 256 bytes to prevent DoS; anything longer triggers an early return — which also returns *.

2016 – OpenSSH timing fix

To prevent user-enumeration via timing (CVE-2016-6210), sshd borrows another account's salt to hash the supplied password even for accounts that don't exist or have no real hash. The comparison then runs between the result of crypt(oversized_pw) — which returns * — and the stored * sentinel. They match.

2022 – Alpine 3.16

To allow SSH key logins without PAM, Alpine switches root's shadow entry from ! to *. This unknowingly moves root into the vulnerable class. The default alpine user on AWS and Azure images follows the same pattern.

2026 – Bug rediscovered

Özgün Kültekin finds the bypass while fuzzing sshd across different libc and PAM combinations. The bug has been silently exploitable since at least 2016.

Am I Affected? — Quick Check

  • Running Alpine Linux with the default openssh-server package? Yes.
  • Running an Alpine-based Docker image that installs and runs sshd? Likely yes.
  • Running OpenWrt, Buildroot or Yocto with sshd and no PAM? Check your musl version and openssh version.
  • Running Debian, Ubuntu, RHEL, or any glibc distro? Not affected.
  • Running OpenBSD? Not affected.
  • Using Dropbear instead of OpenSSH? Not affected.
  • Using PAM (UsePAM yes)? Not affected.

Check your shadow file for star entries: grep ':\*:' /etc/shadow. Any account listed there is in scope if the other conditions are met.

Remediation

Primary fix — update OpenSSH:

# On Alpine
apk update && apk upgrade openssh

# Verify the patched version is installed
ssh -V   # should show OpenSSH_10.6p2 or later
rc-service sshd restart

Interim mitigation — disable password authentication:

echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/00-no-passwords.conf
rc-service sshd restart

Either fix independently closes the attack path. If you manage Alpine Docker images, rebuild them on the patched openssh-server package. Cloud images (Alpine on AWS / Azure) should be patched or replaced.

Verification Steps

  • Confirm fixed version: apk info openssh-server | grep openssh — expect 10.6p2-r0 or later
  • Confirm password auth is off or confirm the patch: sshd -T | grep passwordauthentication
  • Check for signs of exploitation — look for successful logins to key-only accounts in /var/log/auth.log or /var/log/messages
  • Run the detection script: bash <(curl -s https://raw.githubusercontent.com/oz9un/sshole/main/check.sh)
  • Review last and lastb output for unexpected successful logins, especially for root or deploy users