OpenSSH + musl без PAM — обход парольной аутентификации (Alpine, 05.11.2026)
Суть
При сборке/запуске sshd без PAM на musl проверка пароля через crypt() может принять вход для учёток с невалидным полем пароля * (нет пароля / key-only), если атакующий отправляет слишком длинный пароль: musl crypt() в ошибке возвращает *, сравнение с хранимым * проходит. Нужен ещё хотя бы один локальный $5$/$6$ хеш (sshd берёт salt у другой учётки). Учётки с нормальным хешем пароля этим путём не обходятся. OpenSSH: корень вне sshd; workaround в 10.6p2.
Затронуто и исправлено
Условия (Alpine, все сразу): openssh-server без PAM или openssh-server-pam + UsePAM no; PasswordAuthentication yes; учётка с * в shadow (root с ~3.16, adduser без пароля, alpine в tiny-cloud, nss через musl-nscd без shadow); ≥1 локальный $5$/$6$.
Не затронуты: нет sshd (типичный Docker); PasswordAuthentication no; PAM UsePAM yes; shadow ! (locked); нет $-хешей; cloud-init с уже выключенным password auth; glibc-дистрибутивы.
Root: root:* в базе, но дефолт PermitRootLogin prohibit-password не пускает root по паролю. tiny-cloud: alpine + passwordless doas → после входа возможен root.
| Ветка Alpine | openssh с фиксом |
|---|---|
| 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 |
Релизы: 3.21.10 / 3.22.8 / 3.23.8 / 3.24.4. Portable OpenSSH: 10.6p2 (05.11.2026).
Эксплуатация
- Alpine: «one connection, works every time» при выполнении условий; не пишет about in-the-wild.
- OpenSSH: critical workaround; отдельно — про ИИ-находки и более частые релизы (не равнять с confirmed exploitation).
- KEV / CVE ID — нет на 05.11.2026.
- Публичные разборы есть; ссылки на воспроизведение атаки не публикуем.
Как проверить
# Дистрибутив / libc
cat /etc/os-release
# musl?
ldd /bin/ls 2>/dev/null | head
# Версия пакета openssh (Alpine)
apk info -v openssh 2>/dev/null || apk info openssh
# PAM в рантайме
sshd -T 2>/dev/null | grep -i usepam
# Password auth
sshd -T 2>/dev/null | grep -i passwordauthentication
# Есть ли * и $5$/$6$ в shadow (нужен root)
awk -F: '$2=="*" {print $1":*"}' /etc/shadow
awk -F: '$2 ~ /^\$[56]\$/ {print $1}' /etc/shadowКритерий закрытия: версия пакета ≥ строки в таблице для вашей ветки и sshd перезапущен после апгрейда.
Как исправить
# 1) Обновить (предпочтительно)
apk upgrade -U -a
# или точечно пакет openssh / openssh-server по политике
# 2) Обязательно перезапустить демон (apk сам его не подменяет)
rc-service sshd restart
# openrc-статус
rc-service sshd statusИнвентаризация: все Alpine-хосты и VM с sshd наружу; CI-образы с sshd; tiny-cloud AWS/Azure/qcow до 3.21.10 / 3.22.8 / 3.23.8 / 3.24.4 — обновить на месте. Закреплённые старые пакеты / зеркала без security — снять pin.
Если обновить нельзя
- Убедиться, что вход по ключу работает; сессию не рвать.
- Выставить
PasswordAuthentication no(drop-in в/etc/ssh/sshd_config.d/на 3.19+ и/или правка основного конфига) →sshd -t→ restart →sshd -T | grep passwordauthentication→ ожидаетсяno. - Не полагаться на «пароль сложный»: дыра как раз для учёток без пароля (
*).
Признаки взлома и что делать
Alpine: в /var/log/messages ищите Accepted password for по учёткам с * (alpine, root, key-only). Аудит doas, новых пользователей, authorized_keys, недавних файлов. Нельзя исключить вход — считать хост скомпрометированным: ротация ключей/секретов, пересоздание из проверенного образа, не только патч sshd.