14 min to read
SSHole: The OpenSSH Bug That Turns "No Password" Into Root
A long enough password unlocks accounts that were never meant to take one. Here is who is exposed and how to close it today.
We assume an account with no password is a safe account. On Alpine Linux, root ships with a * in its password field precisely so that nobody can log in with a password. Key-only deploy users are created the same way. That assumption broke on 5 November 2026.
A flaw now tracked as CVE-2026-123158 and nicknamed “SSHole” lets an unauthenticated attacker log in over SSH to exactly those accounts, simply by sending a password that is between 257 and 1,024 bytes long. OpenSSH shipped an emergency release, 10.6p2, the same day. Within hours, public proof-of-concept code appeared, and by the next morning researchers at Previdian said they had watched an attacker get root on an Alpine honeypot using it.
The good news: the bug only bites a specific combination of settings. The bad news: that combination is the default on Alpine, which sits underneath a huge share of the Docker images, appliances and embedded devices in use today. This post explains who is affected, how to check in minutes, and what to fix first.
TL;DR / Key takeaways
- What: CVE-2026-123158 (“SSHole”, CVSS 8.1) is an authentication bypass in OpenSSH on Linux systems built with the musl C library and without PAM. An oversized password (257–1,024 bytes) is accepted for accounts whose password hash is
*.- Who: Alpine Linux (every release checked, 3.16 to edge), plus other musl-based builds such as some Buildroot and Yocto images, and any Docker container that runs
sshdon Alpine. glibc distributions (Debian, Ubuntu, RHEL, Fedora), PAM-enabled setups, OpenBSD and Dropbear are not affected.- Exploitation: Public PoCs appeared within hours. Previdian reported root access on an Alpine honeypot less than nine hours after the CVE was published, and has since logged 19 attempts from 12 IPs.
- Fix: Upgrade to OpenSSH 10.6p2 or your distro’s patched package (Alpine:
apk upgrade -U -a), then restart sshd. Quick workaround:PasswordAuthentication no.- Then check: Search logs for
Accepted password foron accounts that should never accept passwords. If you cannot rule out a login, treat the host as compromised.
Timeline
- July 2016: OpenSSH adds a fix for a username-enumeration issue (CVE-2016-6210). It makes
sshdhash a password against a borrowed salt even for accounts that cannot log in, so that timing does not reveal which users exist. This is the code path SSHole abuses. - Early November 2026: Researcher Özgün Kültekin of Wakefield finds the bug and reports it. The OpenSSH team fixes it within about a day.
- 5 November 2026: OpenSSH 10.6p2 is released as a “critical security workaround”. Alpine publishes fixed packages for every branch from 3.16 to edge. The CVE is published, and public PoC code follows the same evening (UTC).
- 6 November 2026: Previdian reports the first exploitation attempt less than nine hours after the CVE went public, including confirmed root access on an Alpine honeypot.
- 6–7 November 2026: Previdian’s sensors record 19 attempts from 12 unique IPs in eight countries.
How SSHole works (conceptually)
To understand the bug you need three small facts.
1. * means “no password”. In /etc/shadow, a password field of * means the account cannot log in with a password at all. It is how Alpine marks root, how adduser marks a user created without a password, and how many key-only service accounts are set up.
2. sshd still hashes your password for those accounts. Since the 2016 enumeration fix, when PAM is not in use, sshd takes the salt from another account on the system that has a real hash (a $5$ or $6$ entry) and runs your password through crypt() anyway. The result is then compared with what is stored in /etc/shadow.
3. musl’s crypt() returns * when it fails. musl rejects passwords longer than 256 bytes for these hash types. When it does, it does not return an empty string or crash. It returns the string *.
Put those together: the attacker sends a 257-byte (or longer, up to 1,024) password for an account stored as *. musl fails, returns *, and sshd compares * with *. They match, and the login is accepted. The OpenSSH team noted that they consider “the root cause of this bug to be outside sshd, but it is exploitable via sshd and can be avoided there.” Their fix makes sshd refuse a match when the stored hash field is shorter than four characters.
Important limits, which come straight from the advisories:
- Accounts with a real password hash are not bypassed. The trick only works against
*accounts. - Accounts locked with
!instead of*are not affected. - The system needs at least one other account with a
$5$or$6$hash, because that is where the borrowed salt comes from. A fresh Alpine install with no password set anywhere is safe until someone sets one. PermitRootLogin prohibit-passwordprotectsrootdirectly, but not a*user who can become root, for example throughdoas.
As Safe Security summarised it: “A successful SSHole attack grants access with the compromised account’s existing permissions.” On Alpine, that is very often root.
Why cloud images are a special case
Alpine’s advisory flags its official “tiny-cloud” images for AWS and Azure. Images from 3.18.11 through 3.24.3 ship an alpine user with a * password and passwordless doas. They are safe on first boot because no $6$ hash exists yet. The moment an admin sets a console password for any account, the box becomes exploitable, and the alpine user can become root. Images built from 3.21.10, 3.22.8, 3.23.8, 3.24.4 and later include the fix.
Real-world exploitation
The disclosure-to-attack window here was measured in hours, not weeks.
- On 5 November, The Hacker News reported that no attacks had been seen. Safe Security wrote on 6 November that in-the-wild exploitation “has not been established”.
- Previdian, which runs honeypot sensors, reports its first exploitation attempt less than nine hours after the CVE was published and confirmed root access on an Alpine Linux + OpenSSH honeypot. The Hacker News reported one source IP,
89.116.89[.]18(geolocated to Japan), that sent oversized passwords and then ranid,unameandhostnameover non-interactive SSH sessions, classic “did it work?” reconnaissance. - Previdian’s tracker now lists 19 attempts from 12 unique IPs across eight countries on 6 and 7 November. Previdian is careful to note that its sensor events “do not show that the target was vulnerable or that the attempt succeeded.”
- Several public PoC and scanner repositories exist on GitHub. Previdian warns they “could contain malware”, which is a good reason not to run random PoCs on production networks.
No named threat actor has been linked to the activity so far, and details are still emerging. The pattern is familiar: mass scanning of port 22 with a cheap one-shot payload.
Am I affected?
You are potentially vulnerable only if all four of these are true on a host (per the Alpine advisory):
sshdis OpenSSH built without PAM, or the PAM build withUsePAM no, on a musl system.PasswordAuthentication yes(the default).- At least one account has
*as its password hash. - At least one other account has a
$5$or$6$hash.
You are not affected if you run glibc distributions such as Debian, Ubuntu, RHEL or Fedora; use PAM (UsePAM yes); use Dropbear (the OpenWrt default) instead of OpenSSH; run containers that do not run sshd; or already have PasswordAuthentication no. Alpine notes that cloud-init images write PasswordAuthentication no and are therefore not exposed.
Quick checks you can run on a suspect host (read-only, as root):
~|#| 1. Is this a musl system, and which OpenSSH is installed?
ldd ~|-|~|-|version 2>&1 | head ~|-|1 ~|#| mentions "musl" on Alpine
apk info ~|-|v openssh~|-|server 2>/dev/null
ssh ~|-|V
~|#| 2. What is the effective password~|-|auth setting?
sshd ~|-|T | grep ~|-|Ei '^(passwordauthentication|usepam|permitrootlogin)'
~|#| 3. Which accounts use "*" and which have real $5$/$6$ hashes?
awk ~|-|F: '$2~|=|~|=|"*"{print "star:", $1}' /etc/shadow
awk ~|-|F: '$2 ~ /^\$[56]\$/{print "hash:", $1}' /etc/shadow
If step 2 shows passwordauthentication yes and step 3 lists both star: and hash: entries, patch now.
For containers, the question is simply whether the image starts sshd. Check your Dockerfiles and compose files for openssh-server and an sshd entrypoint, or run pidof sshd inside running containers.
Alpine’s fixed openssh packages, per branch: edge 10.6_p2-r0, 3.24 10.3_p1-r2, 3.23 10.2_p1-r1, 3.22 10.0_p1-r11, 3.21 9.9_p2-r1, 3.20 9.7_p1-r6, 3.19 9.6_p1-r3, 3.18 9.3_p2-r4, 3.17 9.1_p1-r7, 3.16 9.0_p1-r6. Point releases 3.21.10, 3.22.8, 3.23.8 and 3.24.4 include the fix. Note that only edge moves to 10.6p2; the stable branches received a backport, so do not judge exposure by the upstream version string alone.
Detection: did someone already get in?
Normal SSH logs do not record password length, so you cannot see the oversized password itself. What you can see is the result: a password login on an account that should never accept one.
~|#| Alpine default syslog location
grep 'Accepted password for' /var/log/messages
~|#| Narrow to accounts that should be key~|-|only or passwordless
grep ~|-|E 'Accepted password for (root|alpine|deploy|git)\b' /var/log/messages
Adjust the user list to match your key-only accounts. In a SIEM, the same idea looks like this:
~|#| Splunk
index~|=|linux sourcetype~|=|syslog "Accepted password for"
| rex "Accepted password for (?~|<|user>\S+) from (?~|<|src_ip>\S+)"
| search user IN ("root","alpine","deploy","git")
| stats count min(_time) as first max(_time) as last by host user src_ip
~|#| Microsoft Sentinel (Syslog table)
Syslog
| where ProcessName ~|=|~|=| "sshd" and SyslogMessage has "Accepted password for"
| extend user ~|=| extract(@"Accepted password for (\S+)", 1, SyslogMessage),
src_ip ~|=| extract(@"from (\S+)", 1, SyslogMessage)
| where user in ("root","alpine","deploy","git")
| summarize count(), min(TimeGenerated), max(TimeGenerated) by Computer, user, src_ip
Also review, as Alpine recommends:
doasusage and/etc/doas.confchanges.- Recently created users and changes to
/etc/passwdand/etc/shadow. - New or modified
authorized_keysfiles (attackers often add a key for persistence). - Recently changed files in
/etc,/root, cron directories and web roots. - Connections from the published source IP
89.116.89[.]18. Treat a single IP as a weak indicator; scanning infrastructure rotates.
Alpine’s guidance is unambiguous: “If you cannot rule out a login, treat the machine as compromised.” For a container, that means rebuild from a clean, patched image and rotate any secrets it held. For a VM or appliance, it means incident response, not just a patch.
What to do: a prioritised checklist
Immediately (today)
- Patch and restart. On Alpine:
apk upgrade -U -a, thenrc-service sshd restart. The upgrade does not restart the running daemon, so an unrestarted host stays vulnerable. Elsewhere, install OpenSSH 10.6p2 or your vendor’s fixed build. -
If you cannot patch right now, turn off password auth. OpenSSH’s Damien Miller put it plainly: “set PasswordAuthentication=no or disable SSH for these systems IMMEDIATELY.” Alpine suggests a drop-in file (read by default from 3.19):
echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00~|-|no~|-|passwords.conf sshd ~|-|t && rc~|-|service sshd restart sshd ~|-|T | grep ~|-|i passwordauthentication ~|#| expect: passwordauthentication noCheck
Matchblocks insshd_configtoo: aMatchsection can switch password auth back on for some users. - Run the log check above on every affected host and escalate any unexpected
Accepted passwordline.
This week
- Rebuild container images. Any Alpine-based image that runs
sshdmust be rebuilt on a fixed base and redeployed. Patching a running container does not fix the image the next one starts from. - Inventory musl systems beyond Alpine: Buildroot and Yocto-based devices, network and storage appliances, CI runners and “jump box” containers. Ask embedded vendors whether they are affected.
- Rebuild or re-image Alpine cloud instances created from tiny-cloud images 3.18.11 to 3.24.3, or at least patch them and confirm no console password has been set on a
*account with passwordlessdoas. - Take SSH off the internet where it does not need to be there. Put it behind a VPN, bastion or identity-aware proxy, and restrict source IPs at the firewall or security group.
Longer term
- Make key-only the norm. Set
PasswordAuthentication noandKbdInteractiveAuthentication noin your baseline images unless you have a specific reason not to, and test it in CI withsshd -T. - Stop shipping
sshdin application containers. Usedocker exec,kubectl execor your platform’s session tooling instead. A container that does not runsshdis not affected by this bug or the next one. - Prefer
!for locked accounts and review how your build tooling creates service users.
What this means for Pakistan and South Asia
Alpine is the default base for a large share of the Docker images that startups, fintechs and e-commerce teams in Pakistan deploy on local VPS providers and in AWS or Azure regions nearby. Debug containers running sshd and VPS images with password login enabled are easy to forget about. The same goes for network gear, NVRs and IoT boxes built on Buildroot or Yocto that are rarely patched once installed. If your team builds or sells such devices, check your firmware now; your customers will not know to.
OffensioX perspective
SSHole is a textbook example of how security bugs live in the seams between components. musl’s behaviour was documented a decade ago. OpenSSH’s anti-enumeration fix was a sensible security improvement. Alpine’s choice of * for root was reasonable. Each decision was defensible on its own; combined, they produced a remote root login that no one noticed for years. It also shows how much the window has shrunk: public PoC within hours, honeypot compromise the next morning. The lessons we would underline are hygiene ones that would have prevented exposure regardless of this specific bug: no password authentication on internet-facing SSH, no sshd inside application containers, and an asset inventory good enough to answer “where do we run Alpine with SSH?” in minutes rather than days.
If you are not sure what your SSH exposure looks like across servers, containers and appliances, OffensioX’s penetration testing and security assessments can map it and verify your fixes hold.
References
- Alpine Linux – OpenSSH password authentication bypass advisory (5 Nov 2026)
- OpenSSH – Release notes, OpenSSH 10.6p2
- Previdian – CVE-2026-123158 tracker and telemetry
- The Hacker News – Hackers Target OpenSSH SSHole Flaw Within Hours of Disclosure, Gain Root on Honeypot
- The Hacker News – OpenSSH Issues Emergency Fix for Flaw
- Safe Security – SSHole: High-severity OpenSSH vulnerability turns disabled passwords into unauthorized access
- SOCRadar – SSHole OpenSSH password bypass on Alpine
- Özgün Kültekin – SSHole researcher write-up
- CVE Record – CVE-2026-123158
- Full Disclosure – CVE-2016-6210 OpenSSH username enumeration (2016)