Skip to main content

Security advisory · OpenSSH 10.6p2 · Disclosed 2026-11-05

*sshole

Severity · CriticalNo CVE assignedmusl libc · OpenSSH < 10.6p2

Published

On OpenSSH built against musl libc and checking passwords without PAM, any account whose /etc/shadow field is exactly * accepts any password of 257 to 1024 characters. That covers key-only users, directory users, a locked root and the alpine user in Alpine's cloud images. It takes one connection and no retries.

Disclosed and fixed on 2026-11-05 in OpenSSH 10.6p2, with backported Alpine packages for every branch from 3.16 to edge. No CVE was assigned at disclosure. glibc hosts, and hosts that authenticate through PAM, are not affected.

Impact
Password login as any account whose shadow field is *
Affected
OpenSSH on musl (Alpine, OpenWrt, buildroot, yocto), password auth without PAM
Fixed in
OpenSSH 10.6p2, or your distro's package release
Needs
A reachable * account and one $5$ or $6$ password hash
Reported by
Özgün Kültekin of Wakefield

[01]

One error string, every * account

How it works

* equals *

Without PAM, sshd checks a password by hashing the attempt with crypt() and comparing the result to the stored field. A * field holds no salt, so sshd borrows one from an account that has a $5$ or $6$ hash. musl's crypt() returns * as an error when the key runs past 256 bytes, and that matches the stored *.

musl crypt("A"×256, "$6$salt$")  →  $6$salt$<88-char hash>   (rejected)
musl crypt("A"×257, "$6$salt$")  →  *                        (== stored "*")

OpenSSH 10.6p2 rejects any stored hash shorter than four characters. The root cause is in musl, but sshd is where it is exploitable, so that is where it is fixed.

Fixed Alpine releases

The fixed packages per branch, from Alpine's advisory as of 2026-11-05:

Alpine branchFixed openssh release
edge10.6_p2-r0
3.2410.3_p1-r2
3.2310.2_p1-r1
3.2210.0_p1-r11
3.219.9_p2-r1
3.209.7_p1-r6
3.199.6_p1-r3
3.189.3_p2-r4
3.179.1_p1-r7
3.169.0_p1-r6

Alpine 3.21.10, 3.22.8, 3.23.8 and 3.24.4 ship the fixed package. 3.16 to 3.21 are past regular support, but each got a fixed package.

[A]Mitigations

  • Upgrade openssh and restart sshd: upgrading the package does not replace the running daemon.
  • Stopgap: PasswordAuthentication no in an sshd_config.d drop-in.
  • Stopgap on Alpine: the openssh-server-pam build with UsePAM yes.
  • Containers: rebuild the image on a fixed openssh rather than patching the running container.

[B]Signs of a login

Look in /var/log/messages for sshd lines reading Accepted password for an account that has no password: alpine, root or any key-only user. If you cannot rule a login out, treat the machine as compromised.

[C]Known limits

  • An Alpine branch outside the table cannot be proven patched from its version.
  • A non-Alpine musl build (OpenWrt, buildroot) whose vendor backported the fix keeps the upstream version, so it reads as vulnerable below 10.6p2.

[02]

Check and fix with Swamp

  1. Step 1:Am I affected?

    A machine is exposed only when all of these hold at once.

    • musl libc: Alpine, OpenWrt, buildroot, yocto.
    • An unfixed OpenSSH: below 10.6p2 upstream, or below your distro's fixed package release.
    • No PAM on the password path: Alpine's openssh-server has none; openssh-server-pam with UsePAM yes is safe.
    • PasswordAuthentication yes, the OpenSSH default.
    • A reachable * account. A * root counts only under PermitRootLogin yes, but any other * account that can become root, for example through doas, is as good as root.
    • A salt seed: at least one account with a $5$ or $6$ hash. A fresh box with every account locked is safe until the first real password is set.

    Distributions backport the fix without changing the OpenSSH version, so ssh -V cannot tell patched from vulnerable: the package release decides. Alpine's tiny-cloud images from 3.18.11 through 3.24.3 ship an alpine user with a * field and passwordless doas; they become exploitable as soon as any $6$ hash exists.

  2. Step 2:Install Swamp and the scanner

    The @swamp/cve/sshole extension has one method, scan. It reads the target and reports a verdict. It never logs in over SSH, never tries a password and never changes the target.

    Install Swamp:

    curl -fsSL https://swamp-club.com/install.sh | sh

    Initialize a new repo (run this in a new empty directory):

    swamp repo init

    Pull the scanner:

    swamp extension pull @swamp/cve/sshole

    Create a scanner for this machine:

    swamp behavior create @swamp/cve/sshole sshole-scanner

    Am I vulnerable (run as root so it can read /etc/shadow):

    swamp behavior run sshole-scanner scan
  3. Step 3:Scan a container or an image

    Set dockerContainer to scan a running container through docker exec, or dockerImage to start a container from an image, scan it and remove it. Set one or the other, not both; with neither, scan reads the local machine.

    swamp behavior create @swamp/cve/sshole sshole-web \
        --global-arg dockerContainer=web
    
    swamp behavior run sshole-web scan
    swamp behavior create @swamp/cve/sshole sshole-app-image \
        --global-arg dockerImage=registry.example.com/app:1.4
    
    swamp behavior run sshole-app-image scan

    ›Scan results

    Three images built for this page: Alpine 3.15 on openssh 8.8 with a key-only deploy account, Alpine 3.20 on the fixed 9.7_p1-r6, and Debian bookworm.

    # *sshole (OpenSSH musl password bypass)
    
    1 target(s) scanned — 1 vulnerable, 0 not vulnerable, 0 unknown.
    
    ┌─────────────────────────┬────────────┬──────────┬──────┬───────────────┬─────────┬────────┬───────────┐
    │ Target                  │ Verdict    │ Risk     │ libc │ OpenSSH       │ PAM     │ PwAuth │ '*' accts │
    ├─────────────────────────┼────────────┼──────────┼──────┼───────────────┼─────────┼────────┼───────────┤
    │ sshole-demo:alpine-3.15 │ VULNERABLE │ CRITICAL │ musl │ OpenSSH_8.8p1 │ unknown │ yes    │ 1         │
    └─────────────────────────┴────────────┴──────────┴──────┴───────────────┴─────────┴────────┴───────────┘
    
    ## sshole-demo:alpine-3.15 — VULNERABLE
    
    Exposed: 1 reachable '*' account(s) (deploy) accept any 257–1024 character
    password.
    
    '*' accounts:
    
        * `deploy`
        * sshd is not linked against / does not have PAM for password auth.
        * 1 reachable '*' account(s); on musl a password over 256 bytes hashes to '*' and
          matches.
        * 1 account(s) carry a $5$/$6$ hash to seed the borrowed salt.
    
    ## Recommendations
    
        * Upgrade openssh to 10.6p2 or newer (apk upgrade -U -a) and restart sshd.
        * This looks like a container: rebuild the image on a fixed OpenSSH rather than patching the running container.
    
    ## Stopgaps until patched
    
        * Set 'PasswordAuthentication no' in /etc/ssh/sshd_config.d/00-sshole.conf and restart sshd.
        * On Alpine, the openssh-server-pam build with 'UsePAM yes' is not affected.
    
    Fixed in OpenSSH 10.6p2 (2026-11-05). No CVE was assigned at disclosure. glibc
    hosts and hosts that authenticate through PAM are not affected.
    # *sshole (OpenSSH musl password bypass)
    
    1 target(s) scanned — 0 vulnerable, 1 not vulnerable, 0 unknown.
    
    ┌─────────────────────────┬────────────────┬──────┬──────┬───────────────┬─────────┬────────┬───────────┐
    │ Target                  │ Verdict        │ Risk │ libc │ OpenSSH       │ PAM     │ PwAuth │ '*' accts │
    ├─────────────────────────┼────────────────┼──────┼──────┼───────────────┼─────────┼────────┼───────────┤
    │ sshole-demo:alpine-3.20 │ NOT VULNERABLE │ NONE │ musl │ OpenSSH_9.7p1 │ unknown │ yes    │ 2         │
    └─────────────────────────┴────────────────┴──────┴──────┴───────────────┴─────────┴────────┴───────────┘
    
    ## sshole-demo:alpine-3.20 — NOT VULNERABLE
    
    Patched: 9.7_p1-r6 carries the fix.
    
        * patched: Alpine 3.20 openssh 9.7_p1-r6 includes the fix (≥ 9.7_p1-r6, Alpine 3.20).
    
    Fixed in OpenSSH 10.6p2 (2026-11-05). No CVE was assigned at disclosure. glibc
    hosts and hosts that authenticate through PAM are not affected.
    # *sshole (OpenSSH musl password bypass)
    
    1 target(s) scanned — 0 vulnerable, 1 not vulnerable, 0 unknown.
    
    ┌─────────────────────────────┬────────────────┬──────┬───────┬─────────────┬─────┬────────┬───────────┐
    │ Target                      │ Verdict        │ Risk │ libc  │ OpenSSH     │ PAM │ PwAuth │ '*' accts │
    ├─────────────────────────────┼────────────────┼──────┼───────┼─────────────┼─────┼────────┼───────────┤
    │ sshole-demo:debian-bookworm │ NOT VULNERABLE │ NONE │ glibc │ OpenSSH_9.2 │ yes │ yes    │ 19        │
    └─────────────────────────────┴────────────────┴──────┴───────┴─────────────┴─────┴────────┴───────────┘
    
    ## sshole-demo:debian-bookworm — NOT VULNERABLE
    
    Not affected: glibc libc.
    
        * libc is glibc; the bypass only affects OpenSSH built against musl.
    
    Fixed in OpenSSH 10.6p2 (2026-11-05). No CVE was assigned at disclosure. glibc
    hosts and hosts that authenticate through PAM are not affected.
  4. Step 4:Read the verdict

    Every scan leads with one of three verdicts, and a risk level from critical to none.

    • vulnerable: every precondition holds, and the report names the * accounts that accept a long password.
    • not-vulnerable: a precondition is missing, such as glibc, a fixed release, PAM or password auth turned off.
    • unknown: the scanner could not decide. Most often it ran without root and could not read /etc/shadow; rerun as root. No sshd at all, or a setting that could only be read from the config files because sshd -T failed, is also unknown. The report says which input was missing.

    To upgrade and restart sshd on Alpine:

    apk upgrade -U -a
    rc-service sshd restart

    If you cannot upgrade yet, turn password logins off. Keep a working key login and your current session open until a new login succeeds. Alpine reads the drop-in directory from 3.19 on; on older releases set it in /etc/ssh/sshd_config instead.

    mkdir -p /etc/ssh/sshd_config.d
    echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00-sshole.conf
    sshd -t && rc-service sshd restart
    sshd -T | grep -i passwordauthentication

    ›An unknown result

    # *sshole (OpenSSH musl password bypass)
    
    1 target(s) scanned — 0 vulnerable, 0 not vulnerable, 1 unknown.
    
    ┌─────────────────────────────────┬─────────┬──────┬──────┬───────────────┬─────────┬─────────┬───────────┐
    │ Target                          │ Verdict │ Risk │ libc │ OpenSSH       │ PAM     │ PwAuth  │ '*' accts │
    ├─────────────────────────────────┼─────────┼──────┼──────┼───────────────┼─────────┼─────────┼───────────┤
    │ sshole-demo:alpine-3.15-nonroot │ UNKNOWN │ HIGH │ musl │ OpenSSH_8.8p1 │ unknown │ unknown │ ?         │
    └─────────────────────────────────┴─────────┴──────┴──────┴───────────────┴─────────┴─────────┴───────────┘
    
    ## sshole-demo:alpine-3.15-nonroot — UNKNOWN
    
    Likely exposed: musl, non-PAM, password auth on — rerun as root to list '*'
    accounts and the salt seed.
    
        * sshd is not linked against / does not have PAM for password auth.
        * /etc/shadow is not readable, so '*' accounts were not listed.
    
    Not checked: PasswordAuthentication (defaults to on); salt-seeding $5$/$6$
    account (need root to read /etc/shadow).
    
    ## Recommendations
    
        * Rerun as root to confirm the exposed accounts.
        * Upgrade openssh to 10.6p2 or newer (apk upgrade -U -a) and restart sshd.
    
    ## Stopgaps until patched
    
        * Set 'PasswordAuthentication no' in /etc/ssh/sshd_config.d/00-sshole.conf and restart sshd.
        * On Alpine, the openssh-server-pam build with 'UsePAM yes' is not affected.
    
    Fixed in OpenSSH 10.6p2 (2026-11-05). No CVE was assigned at disclosure. glibc
    hosts and hosts that authenticate through PAM are not affected.

Find every exposed sshd

Pull the scanner and run it on every Alpine host, container and image you ship.

swamp extension pull @swamp/cve/sshole