← Übersicht

OpenSSH: Passwort-Bypass auf musl ohne PAM ("sshhole", CVE-2026-123158)

Stand: 06.11.2026

Der Entdecker nennt die Lücke "*sshole" (Website sshhole.com, Repo github.com/oz9un/sshole), in der Presse heißt sie meist "SSHHole" oder "sshhole". sshd nimmt auf Linux-Systemen mit musl-libc und ohne PAM für Accounts mit * im Passwortfeld jedes Passwort mit 257 bis 1024 Bytes an. Betroffen sind vor allem Alpine Linux und Container- bzw. Cloud-Images auf Alpine-Basis. Systeme mit glibc (Debian, Ubuntu, RHEL, Fedora, SUSE) sind nicht betroffen.

Worum es geht

Ohne PAM prüft sshd Passwörter selbst in sys_auth_passwd() (auth-passwd.c): Es hasht das eingegebene Passwort mit crypt() und vergleicht das Ergebnis per strcmp() mit dem Eintrag aus /etc/shadow. Drei Verhaltensweisen greifen dabei ineinander:

  1. OpenSSH seit 2016 (Fix für CVE-2016-6210): Damit Angreifer gültige Benutzernamen nicht an der Antwortzeit erkennen, hasht sshd auch dann, wenn der gespeicherte Eintrag kein gültiger Hash ist (z. B. *). Das Salt dafür holt pick_salt() (openbsd-compat/xcrypt.c) von einem anderen Account mit echtem Hash, etwa $6$... (SHA-512).
  2. musl crypt() seit 2012: Im Fehlerfall gibt musl den String "*" zurück, nicht NULL, wie POSIX es vorschreibt (musl-Commit b3c4cc12).
  3. musl SHA-256/SHA-512-crypt seit 2012: Schlüssel über 256 Bytes werden abgelehnt (KEY_MAX 256, musl-Commit 88bf5a8a). crypt() schlägt fehl und liefert "*".

Ergebnis: Für einen Account mit * in /etc/shadow hasht sshd ein 257 Byte langes Passwort mit dem $6$-Salt eines anderen Accounts. musl liefert "*", sshd vergleicht "*" mit "*", und der Login ist erfolgreich. Über 1024 Bytes verwirft sshd das Passwort vorher (MAX_PASSWORD_LEN 1024), daher die Obergrenze.

Glibc bzw. libxcrypt geben im Fehlerfall NULL oder "*0" zurück, das passt nie auf *. Mit PAM prüft pam_unix und nicht sys_auth_passwd().

Der Fix

OpenSSH 10.6p2 vom 05.11.2026 enthält den Upstream-Commit 90f9c2f7 ("avoid password authn bypass", Damien Miller). sshd akzeptiert ein Passwort nur noch, wenn der gespeicherte Hash mindestens 4 Zeichen lang ist (MIN_PASSWORD_HASH_LEN). Einträge wie *, ! oder x passen damit nie mehr. OpenSSH sieht die Ursache in musl, umgeht sie aber in sshd. Die Release Notes nennen es "critical security workaround".

Bewertung

QuelleCVSS 3.1Vektor
IONIX, OX Security8.1 (High)AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

Die hohe Angriffskomplexität kommt von den vielen Vorbedingungen (siehe unten). Ein Eintrag bei NVD und im Alpine-Security-Tracker war am 06.11. noch nicht abrufbar.

Ausnutzung

Vorbedingungen

Alle Punkte müssen zutreffen:

#BedingungBemerkung
1sshd ist gegen musl gelinktAlpine, OpenWrt mit openssh-server, Buildroot/Yocto mit musl, Void-musl usw.
2sshd läuft ohne PAMStandard bei Alpines openssh-server und OpenWrts openssh-server. Die -pam-Pakete setzen UsePAM yes.
3PasswordAuthentication yesOpenSSH-Standard, wenn nichts anderes konfiguriert ist
4Ein Account hat * in /etc/shadowsiehe unten
5Ein anderer Account hat einen $5$- oder $6$-Hashliefert das Salt. Ohne ihn nimmt sshd das DES-Salt xx, DES kürzt auf 8 Zeichen und schlägt nicht fehl.
6sshd lässt den *-Account zuPermitRootLogin prohibit-password (Standard) sperrt root für Passwörter, AllowUsers/DenyUsers können ebenfalls schützen

Warum * auf Alpine so häufig ist

sshd ohne PAM behandelt ! am Anfang des Hashes als gesperrten Account und lehnt dann auch Key-Logins ab. Wer auf Alpine einen reinen Key-Account braucht, setzt deshalb * (z. B. echo "user:*" | chpasswd -e oder usermod -p '*'). Das ist eine verbreitete Empfehlung für Alpine und steht so in vielen Dockerfiles.

Was betroffen ist

Betroffen

Nicht betroffen

Beispiel Gitea und Forgejo

Die offiziellen Docker-Images von Gitea und Forgejo basieren auf alpine:3.24, installieren openssh und setzen echo "git:*" | chpasswd -e. Das sshd-Template von Gitea setzt aber PasswordAuthentication no. Im Standard sind sie damit nicht angreifbar. Wer die Template-Variable auf yes gestellt hat, sollte die Images trotzdem neu ziehen.

Gefixte Versionen

OpenSSH upstream

VersionStatus
10.6p2 (05.11.2026)gefixt
alle Portable-Versionen davor seit 2016verwundbar unter den oben genannten Bedingungen

Alpine Linux

Alpine hat am 05.11.2026 alle Branches ab 3.16 gepatcht, auch die nicht mehr unterstützten. Ältere Versionen (z. B. 9.3_p2) haben den Upstream-Commit als pw.patch bekommen. Geprüft am 06.11. in den APKBUILD-Dateien der aports-Branches:

Branchgefixt in openssh
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
3.15 und älterkein Fix

Wichtig für Scanner: Die secfixes-Liste im APKBUILD (Grundlage der Alpine-secdb) enthält CVE-2026-123158 am 06.11. noch nicht. Trivy, Grype und andere Scanner, die die secdb auswerten, melden die Lücke deshalb unter Umständen noch nicht. Bis dahin hilft nur ein Versionsvergleich mit der Tabelle.

OpenWrt (Feed packages)

Branchopenssh-serverStatus
master10.6_p2-1gefixt
openwrt-25.1210.6_p2-1gefixt
openwrt-24.109.9_p2-2gefixt (Backport 0001-avoid-password-authn-bypass.patch)
openwrt-23.059.8p1-2gefixt (Backport)

OpenWrts Standard-/etc/shadow hat * bei daemon, network und nobody (Shell /bin/false). Erst wenn root ein Passwort mit $5$/$6$-Hash bekommt, ist Bedingung 5 erfüllt.

Prüfen

Ist der sshd verwundbar? Auf dem Host bzw. im Container:

# 1. musl?
ldd --version 2>&1 | head -1            # "musl libc" → musl

# 2. Paketversion (Alpine) mit der Tabelle oben vergleichen
apk info -v openssh-server openssh-server-pam 2>/dev/null

# 3. Effektive sshd-Konfiguration
sshd -T | grep -Ei '^(usepam|passwordauthentication|permitrootlogin|allowusers|denyusers) '

# 4. Accounts mit "*" und Accounts mit $5$/$6$-Hash
awk -F: '$2=="*"{print "STERN:", $1} $2 ~ /^\$[56]\$/ {print "HASH: ", $1}' /etc/shadow

Verwundbar, wenn: musl, Paket älter als die gefixte Version, usepam no, passwordauthentication yes, mindestens ein STERN-Account, der sich anmelden darf, und mindestens ein HASH-Account.

Spuren eines Angriffs:

grep "Accepted password" /var/log/messages /var/log/auth.log 2>/dev/null

Ein Accepted password für einen Account mit * in /etc/shadow ist ein starker Hinweis, denn solche Accounts haben kein Passwort. Die Passwortlänge steht nicht im Log.

Gegenmaßnahmen

  1. Patchen: apk upgrade -U -a und danach rc-service sshd restart. Das Paket-Update allein ersetzt den laufenden Daemon nicht.
  2. Container- und Cloud-Images neu bauen, die auf Alpine basieren und einen sshd enthalten. Ein laufender Container bekommt den Fix nicht durch ein neues Basisimage.
  3. Sofort, falls Patchen nicht geht: Key-Login testen, eine Sitzung offen lassen, dann Passwort-Login abschalten:
    echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/00-sshhole.conf
    rc-service sshd restart

    Die Datei muss alphabetisch vor anderen Drop-ins liegen, weil bei sshd der erste Treffer gilt. Mit sshd -T | grep passwordauthentication prüfen.

  4. Härten: nur Public-Key-Auth, SSH nicht ins Internet exponieren, AllowUsers setzen.

Quellen