Blog

SSHole: a 257-byte password opens key-only accounts on Alpine Linux

On Alpine Linux and other systems that run OpenSSH on the musl C library without PAM, a password longer than 256 bytes logs in to accounts that have no password at all. The flaw, CVE-2026-123158 or SSHole, was fixed on 5 November, and less than a day later attackers had root on a honeypot. If you run sshd on Alpine, update it and restart sshd now.

What happens

Accounts that only log in with a key often have a single asterisk in their password field. By a convention as old as Unix, * is never a valid hash, so no password can match it. On Alpine, root ships like this since about version 3.16, and so do users created with adduser without a password and the alpine user of the official cloud images.

When sshd is built without PAM, as Alpine's openssh-server package is, it checks passwords itself: it hashes the attempt with crypt() and compares the result with the stored field. For a field like *, which holds no salt, it borrows the salt of another account. musl's crypt() refuses SHA-256 and SHA-512 keys longer than 256 bytes, and when it fails, it returns * as an error marker. That is exactly what is stored for the account, so the comparison succeeds. Özgün Kültekin of Wakefield found the flaw and reported it to the OpenSSH project.

Diagram: the attacker sends a password of 257 to 1,024 bytes for an account whose password field is an asterisk; sshd borrows the salt of another account; musl's crypt() fails on the long input and returns an asterisk; sshd compares asterisk with asterisk and accepts the login; the attacker gets a shell with that account's rights. Disabling password authentication stops the first step, OpenSSH 10.6p2 stops the comparison.
Based on the OpenSSH 10.6p2 release notes, the Alpine advisory and the researcher's write-up. Applies only to sshd on musl without PAM.

The attacker needs the name of such an account, access to the SSH port, and at least one other account on the machine with a real $5$ or $6$ hash, because otherwise there is no SHA salt to borrow. Alpine puts it plainly: one user with a password is enough to make every account without one reachable. It takes one connection, works every time and needs no guessing. sshd itself rejects passwords over 1,024 bytes, which is where the window of 257 to 1,024 bytes comes from.

What the attacker gets is a shell with the rights of that account. Root is protected from password logins by the OpenSSH default PermitRootLogin prohibit-password and is only directly reachable with PermitRootLogin yes. That does not help if another account can become root. The tiny-cloud images of Alpine from 3.18.11 to 3.24.3 ship an alpine user with * and passwordless doas. They are safe on first boot and exploitable as soon as any $6$ hash exists, for example because an admin set a console password or user data created a user with a hashed password.

Who is affected

This is not a flaw in OpenSSH everywhere. glibc distributions such as Debian, Ubuntu or RHEL hash differently, and with PAM the password check does not go through this code at all. Affected are Linux systems where sshd runs on musl and checks passwords itself. Alpine is the prominent case, because its standard package is built without PAM and OpenSSH allows password logins by default. Alpine checked every release from 3.16 to 3.24 and edge, and all of them are affected.

Two cards. Affected only if all of these hold: sshd runs on musl, for example Alpine, OpenWrt, Buildroot or Yocto; it checks passwords without PAM; PasswordAuthentication is yes; and there is an account with an asterisk as password plus at least one account with a $5$ or $6$ hash. Not affected: glibc distributions, sshd with UsePAM yes, Dropbear, OpenBSD and other non-Linux systems, containers without sshd, PasswordAuthentication no, and locked accounts marked with an exclamation mark.

musl is not only in Alpine. It is also the C library of OpenWrt and a common choice in firmware built with Buildroot or Yocto, that is, in routers, appliances and embedded devices. OpenWrt uses Dropbear by default, which is not affected, but any device that ships OpenSSH on musl without PAM is worth checking. Containers built on Alpine are only at risk if they run sshd, which most of them do not.

How it came about

The OpenSSH project places the root cause outside sshd, but avoids it in sshd with version 10.6p2. What makes the bug interesting is that every piece was a sensible decision on its own. musl chose in 2012 to return an unmatchable hash instead of NULL, so that programs that do not check for errors cannot crash. OpenSSH started in 2016 to hash every attempt with a real salt, after a researcher had shown that long passwords took measurably longer for existing users than for unknown ones. And Alpine gave root a * instead of a locked field in 2022, so key logins would keep working. Only together do they turn an error marker into a valid password.

Timeline: August 2012 musl's crypt() starts returning an asterisk instead of NULL on failure and limits SHA crypt to 256 bytes; July 2016 OpenSSH borrows salts for accounts without a hash to stop user enumeration; 2019 Alpine's Docker images ship root without a password, CVE-2019-5021, and root gets locked; 2022 Alpine 3.16 sets root to an asterisk; 5 November 2026 OpenSSH 10.6p2 and Alpine's fixed packages.
Dates from the researcher's write-up, the OpenSSH release notes and the Alpine advisory. Each step made sense on its own.

Attacked within hours

OpenSSH 10.6p2 and the advisory of Alpine came out on 5 November, with fixed packages for every Alpine branch back to 3.16, including the ones that are no longer supported. The CVE followed that afternoon, the first public proof of concept less than two hours later. According to the threat intelligence firm Previdian, its sensors saw the first exploitation attempts less than nine hours after the CVE was published. Previdian told The Hacker News that it confirmed root access on a honeypot running Alpine and OpenSSH: an address in Japan sent oversized passwords and then ran id, uname and hostname in non-interactive sessions. Attacks on real victims have not been reported so far, and Safe Security still wrote on 6 November that in-the-wild exploitation had not been confirmed.

Timeline: 5 November OpenSSH 10.6p2 and Alpine's advisory with fixed packages back to 3.16; 15:46 UTC CVE-2026-123158 published; 17:30 UTC first public proof of concept; 6 November 00:05 UTC first exploitation attempts on Previdian's sensors, about eight hours after the CVE; later on 6 November Previdian confirms root access on an Alpine honeypot, the attacker ran id, uname and hostname.
Times from Previdian's CVE page; the honeypot details from Previdian's statement to The Hacker News. Safe Security still wrote on 6 November that no in-the-wild exploitation had been confirmed.

The attack is trivial. There is no memory corruption and no race, just one connection with a long string, and anyone who already scans SSH ports can try it. Standard sshd logs do not record the length of a password, so a successful attack looks like an ordinary password login. That is also the clue: a password login to an account that has no password.

What to do now

  1. Update and restart sshd. On Alpine, apk upgrade -U -a followed by rc-service sshd restart. The package upgrade alone does not replace the running daemon. Fixed are OpenSSH 10.6p2 and the Alpine packages listed in the advisory, for example 10.3_p1-r2 on 3.24, 10.2_p1-r1 on 3.23 and 10.0_p1-r11 on 3.22; the point releases 3.21.10, 3.22.8, 3.23.8 and 3.24.4 contain them.
  2. If you cannot update yet, switch off password logins. Set PasswordAuthentication no, restart sshd and check the effective setting with sshd -T | grep -i passwordauthentication. Make sure your key login works and keep the current session open until you have tested a new one. Alpine's advisory has commands that also cover releases before 3.19, which ignore sshd_config.d.
  3. Do not forget images and running instances. Cloud images and golden images built before the fixed point releases contain the bug. Rebuild them, and update every instance that is already running.
  4. Look beyond Alpine. Make a list of devices and systems that run OpenSSH on musl: appliances, routers, firmware built with Buildroot or Yocto, containers with sshd. On a running system, ldd /usr/sbin/sshd shows ld-musl if sshd uses musl. Ask your vendors whether they ship a fix.
  5. Check whether someone got in. Search /var/log/messages for Accepted password for and compare the accounts with those that have no password, such as alpine, root or key-only users. Review doas use, new users, changed files and authorized_keys. If you cannot rule out a login, treat the machine as compromised, rebuild it and rotate the keys and secrets that were on it.
  6. Do not lock key-only accounts as a quick fix. Replacing * with ! does close the hole for that account, but sshd then refuses key logins as well. The update or PasswordAuthentication no are the right fixes.
  7. Keep SSH off the open internet. A port that only a VPN or a bastion host can reach turns the next pre-authentication flaw from an emergency into a maintenance task.

Do you know which of your systems run sshd on musl? Pentesting and consulting →