OpenSSH sshhole: What to Check on Alpine Linux
OpenSSH 10.6p2 shipped a workaround for sshhole on musl Linux with no PAM. Who is exposed, a five-minute Alpine check, the fix, and why there was no embargo.
TL;DR: The OpenSSH bug going around as “sshhole” needs a musl Linux host (Alpine is the common one), sshd without PAM, password logins on, an account whose shadow password field is exactly
*, and a second account with a real password hash. Check with the five commands below. The fix isPasswordAuthentication noplus an upgrade to the patchedopenssh-serverpackage. As of November 5, no CVE and no exploitation had been reported.
On November 5, 2026, the OpenSSH project released portable 10.6p2. The release notes call it “a critical security workaround that affects Linux systems that use the musl libc and PAM disabled.” A post on X boiled that down to: on Alpine, type a long enough password and you get root. That summary is wrong in a few places. The real exposure takes five conditions at once, and the default Alpine container config blocks the root case.
I’m a CISO and I do fractional security work for small AI startups. I ran the default Alpine setup in Docker on November 5 to see what it does. I did not run the exploit, and nothing below says it works or doesn’t on my lab box. Everything about the exploit mechanism comes from the reporter’s write-up.
Who is exposed?
Damien Miller’s announcement on Hachyderm says non-Linux systems, Linux with glibc, and Linux systems that use PAM for password authentication are not affected. That leaves Linux with musl libc and sshd built without PAM.
The reporter, Özgün Kültekin of Wakefield, published a write-up at sshhole.com. His
account of the mechanism: musl’s crypt() returns
* when it fails, instead of NULL. musl’s SHA-crypt refuses
passwords longer than 256 bytes. So when a user logs in against an
account whose shadow field is exactly *, a password of 257
to 1024 bytes gets treated as a match. The write-up says the
* account “takes any password between 257 and 1024
characters.” It also needs another account on the box with a real
password hash, so there is a salt to work with.
The conditions the write-up lists, all at once:
- sshd running on musl (Alpine, OpenWrt, Buildroot and Yocto all fit)
- no PAM, which is how Alpine’s
openssh-serverships, orUsePAM noelsewhere - password logins turned on
- an account whose shadow field is
*: a key-only user, an LDAP user, or root on Alpine - some other account with a real password hash
The headlines got root wrong. The write-up says Alpine locked root
with ! in 2019 after CVE-2019-5021, then changed root’s
lock to * in Alpine 3.16 in 2022. So root is a
* account on current Alpine. But the sshd_config
man page says, for the prohibit-password setting of
PermitRootLogin: “If this option is set to
prohibit-password (or its deprecated alias, without-password), password
and keyboard-interactive authentication are disabled for root.” That is
the default, so root over SSH with a password needs someone to have set
PermitRootLogin yes. Other * accounts are the
ones to hunt for.
What I saw in a default Alpine container
I started alpine:latest in Docker on an arm64 Linux box,
then ran apk add openssh-server.
/etc/alpine-releasesaid 3.24.3.- Root’s shadow field was
*. It was the only*account in the base image. apkinstalled openssh-server 10.3_p1-r2, which is the fixed Alpine build for 3.24, even though the base image itself is 3.24.3.sshd -Tprintedpasswordauthentication yesandpermitrootlogin prohibit-password, and nousepamline at all. Alpine builds sshd without PAM.ldd /usr/sbin/sshdlisted only musl (ld-musl) and no libpam.adduser -D keyonlygave the new account!in the shadow field, not*. So didpasswd -l.
The two stock ways I tried of making a locked or key-only user both
produced !. A * account mostly shows up when
something else created it: an LDAP or sync tool, a hand-edited shadow
file, a base image or appliance build. My reading is that default Alpine
is a lot harder to hit than the posts suggest. Your hosts may not be
default, so check them.
Is it being exploited?
As of November 5, The Hacker News reports no CVE assigned and no exploitation seen. Miller’s post still says to act fast. In his words, the advice is to “set PasswordAuthentication=no or disable SSH for these systems IMMEDIATELY.” That applies to systems that meet every condition above.
The five-minute check
Start with whether sshd runs at all. Most Alpine containers never start one, and a container built from the Alpine image with no sshd is not exposed through this bug.
pgrep sshd
If something comes back, run these on that host:
cat /etc/alpine-release
ldd /usr/sbin/sshd | grep -E 'musl|pam'
sshd -T | grep -Ei '^(usepam|passwordauth|permitroot)'
awk -F: '$2=="*"{print $1}' /etc/shadow
apk info -v | grep openssh-server
Read the results in order:
lddshows musl and no pam: sshd is the musl, no-PAM kind.sshd -Tshowspasswordauthentication yesand nousepam yes: password logins are on.- The
awkline prints account names whose shadow field is exactly*. Root will be on that list on Alpine. Anything else on it is a candidate. Root is only reachable ifpermitrootloginshowsyes. apk info -vshows which openssh-server build you have. On Alpine 3.24 you want 10.3_p1-r2 or later.
If the first three are all true, treat the host as exposed. It also needs a second account with a real password hash, which is easy to meet on any box where a human ever set a password.
The fix
OpenSSH’s change makes sshd refuse a stored hash shorter than 4
characters. The patch defines MIN_PASSWORD_HASH_LEN 4, and
Miller’s
upstream commit is dated November 5. Alpine picked it up for 3.24-stable,
where the package went from pkgrel 1 to 2 and gained a
pw.patch carrying that commit. Edge
moved to 10.6_p2. The Hacker News lists fixed packages back to
Alpine 3.16 and point releases 3.21.10, 3.22.8, 3.23.8 and 3.24.4. I
only verified the 3.24 commit myself, so check the others against
Alpine’s own package pages.
Turn off password logins first, since that closes the path without waiting on a package, then upgrade. Before you restart sshd, make sure you can still get in with a key.
echo 'PasswordAuthentication no' \
> /etc/ssh/sshd_config.d/00-no-passwords.conf
rc-service sshd restart
apk update
apk upgrade openssh-server
rc-service sshd restart
On Alpine 3.24 the stock sshd_config has an
Include /etc/ssh/sshd_config.d/*.conf line, and in my
container sshd -T showed
passwordauthentication no after adding that file. On an
older or hand-built config, run sshd -T | grep passwordauth
to confirm. If it still says yes, the file isn’t being
read, and the setting goes in the main sshd_config.
Search your auth logs for Accepted password for. No
password should ever work on an account whose shadow field is
*, so any hit for root or a key-only user needs a closer
look. No hits doesn’t clear the host, since logs get rotated or
wiped.
Why a one-day fix matters
The release notes put the root cause outside sshd, and Miller says so
too: “We consider the root cause of this bug to be outside OpenSSH
itself”. So the project shipped a workaround, not a fix to the thing
that returns *. They shipped it fast. His post says, “We
are making this release as fast as possible (we learned of the problem
yesterday)”.
He also explains why there was no long private window: “we have recently observed multiple cases of rapid bug rediscovery by AI agents, sometimes within hours”, and the project will “now consider long embargo periods to be a dangerous practice that primarily benefits attackers.” The write-up says its author used swarms of AI agents for build and login testing.
I covered the finding side of this in the decade-old bug that took a week to find, and the fixing side in Detection Got Cheap. Remediation Didn’t.. I won’t repeat either. This case adds a number from the maintainer: one day from report to release, and no more long embargoes.
For a small team that means the patch window is hours. The part you can prepare is knowing within minutes which hosts run sshd, and which of those are musl without PAM. The five commands above are a first pass at that list. I help small teams build that host list as part of my consulting work.
What I checked on my own stack
This site runs Elixir and Phoenix on Fly.io from a Debian-based
image, which uses glibc, so it is not affected. If your Alpine hosts are
all containers with no sshd, the answer for you may be the same. Confirm
it with pgrep, then go look at routers and embedded builds,
where musl and no PAM are more likely.
For another triage post in the same format, see php2shell: What to Do if Your Servers Run PHP.