ssh-keysign-pwn: как непривилегированный пользователь крадёт ключи хоста — и как это остановить
ssh-keysign-pwn: как непривилегированный пользователь крадёт ключи хоста — и как это остановить
Сервер настроен. Nginx за Cloudflare, fail2ban банит ботов, root-логин по SSH отключён. Публичного доступа к /etc/shadow нет — это же вообще 640, root:shadow. Приватные SSH-ключи хоста лежат в /etc/ssh/ с правами 600. Всё закрыто.
Потом кто-то получает непривилегированный shell — через дырявый плагин, слабый пароль, что угодно — и за несколько секунд читает /etc/shadow и все приватные SSH-ключи хоста. Без sudo и без root. Просто запускает скрипт.
Добро пожаловать в CVE-2026-46333, также известную как ssh-keysign-pwn.
ЧТО СЛУЧИЛОСЬ
14 мая 2026 года Qualys сообщили об уязвимости в ядре Linux, патч был закоммичен в тот же день коммитом 31e62c2ebbfd. В тот же день из публичного коммита появился независимый PoC-эксплойт. Полный advisory Qualys опубликовали 20 мая.
Уязвимость затрагивает не OpenSSH и не конкретную конфигурацию — она в логике ядра Linux.
КАК ЭТО РАБОТАЕТ
Представь охранника который уже сдал пост и пропуск — но ключи от сейфа ещё в кармане. Именно в этот момент кто-то подходит и просит ключи. Формально охранник уже не охранник, проверка провалена.
Примерно так работает CVE-2026-46333.
Ядро Linux использует механизм ptrace — это интерфейс, через который один процесс может наблюдать за другим или обращаться к его ресурсам. Когда привилегированный процесс завершается, ядро освобождает его память (mm) — охранник сдал пропуск. Но файловые дескрипторы к открытым файлам — /etc/shadow, /etc/ssh/ssh_host_*_key — ещё живы. Ключи в кармане.
В этот короткий промежуток функция __ptrace_may_access() пропускает часть проверки: у задачи уже нет контекста памяти, а дескрипторы к чувствительным файлам ещё существуют. Атакующий ловит этот момент через системный вызов pidfd_getfd() — он позволяет скопировать файловый дескриптор из чужого процесса в свой — и получает прямой доступ к файлу.
Два вектора атаки в публичном PoC:
ssh-keysign — это setuid-вспомогательная программа OpenSSH, запускается от root когда SSH-клиенту нужно подписать данные приватным ключом хоста. Устанавливается вместе с openssh-client и присутствует на большинстве Linux-систем. Она открывает приватные ключи хоста (/etc/ssh/ssh_host_*_key) и держит их открытыми на время работы. Атакующий ловит момент завершения ssh-keysign и крадёт дескриптор — приватные ключи прочитаны.
chage — утилита управления сроками паролей. При запуске chage -l система открывает /etc/shadow для чтения данных о пароле пользователя. Тот же race condition — и хеши паролей у атакующего.
Это не переполнение буфера и не сложная цепочка эксплойтов. Просто гонка условий в несколько строк кода.
ЧЕМ ЭТО ОПАСНО
Qualys разработали четыре рабочих эксплойта для одной и той же уязвимости:
chage — читает /etc/shadow с хешами паролей.
ssh-keysign — читает приватные SSH-ключи хоста.
pkexec — выполняет произвольные команды от root. Требует активной консольной сессии, но атакующий может быть залогинен удалённо по SSH.
accounts-daemon — тоже выполняет команды от root, без консоли. Протестировано на дефолтных установках Debian 13 и Fedora.
То есть «просто читает файлы» — это только публичный PoC. Полный root через pkexec и accounts-daemon технически возможен на большинстве дистрибутивов с дефолтной установкой.
Приватный SSH-ключ хоста — это то, чем ваш сервер представляется клиентам при SSH-соединении. Кто владеет этим ключом — может выдать себя за ваш сервер. MITM без предупреждения у клиентов, которые уже добавили ключ в known_hosts. До момента ротации ключей атакующий может перехватывать SSH-сессии незаметно.
Хеши из /etc/shadow можно вынести на отдельную машину и ломать без шума в инфраструктуре. Никаких следов в логах вашего сервера. Если один пароль используется в нескольких местах — компрометация расширяется горизонтально.
Один сервер с непривилегированным доступом превращается в точку входа во всю инфраструктуру.
ЧТО ДЕЛАТЬ ПРЯМО СЕЙЧАС
Шаг 1. Заблокировать эксплойт немедленно — без перезагрузки
Пока ядро не обновлено, два способа закрыть вектор прямо сейчас.
Вариант А — sysctl. Ограничить ptrace на уровне ядра. Значение 2 — достаточная митигация по рекомендации Ubuntu: запрещает ptrace для пользователей без CAP_SYS_PTRACE:
echo kernel.yama.ptrace_scope=2 | sudo tee /etc/sysctl.d/99-CVE-2026-46333.conf
sudo sysctl -p /etc/sysctl.d/99-CVE-2026-46333.conf
Внимание: значение 3 отключает ptrace полностью для всех включая root и не может быть отменено без перезагрузки — не используйте его.
Побочный эффект: непривилегированные пользователи больше не смогут использовать gdb, strace на чужих процессах. На production-серверах это приемлемо.
Вариант Б — снять SUID-бит с обоих векторов атаки:
# Debian/Ubuntu
chmod u-s /usr/lib/openssh/ssh-keysign
chmod u-s /usr/bin/chage
# RHEL/AlmaLinux/Rocky
chmod u-s /usr/libexec/openssh/ssh-keysign
chmod u-s /usr/bin/chage
Это нейтрализует именно те два бинарника которые использует публичный PoC. После установки патча ядра SUID-бит нужно вернуть.
Шаг 2. Обновить ядро
# Debian/Ubuntu
apt update && apt upgrade -y
# RHEL/AlmaLinux/Rocky
dnf clean metadata && dnf upgrade -y
После обновления перезагрузка обязательна — патч в ядре начинает работать только после неё.
Версии ядра с патчем:
Ubuntu 24.04 LTS — патч в версии 6.8.0-117 (USN-8278-1, опубликован 19 мая 2026):
uname -r
# 6.8.0-111-generic → уязвима, обновляться
# 6.8.0-117-generic → патч есть
Ubuntu 22.04 LTS — lowlatency/HWE ядро серии 6.8.x: 6.8.0-117.117.1~22.04.1 (USN-8278-1). Стандартное ядро 5.15.x — следить: https://ubuntu.com/security/CVE-2026-46333
AlmaLinux 10 — начиная с kernel-6.12.0-227.el10 (в продакшн-репозитории с 16 мая).
После перезагрузки с патчем убрать митигации:
sudo rm /etc/sysctl.d/99-CVE-2026-46333.conf
sudo sysctl kernel.yama.ptrace_scope=1
# Debian/Ubuntu
chmod u+s /usr/lib/openssh/ssh-keysign
chmod u+s /usr/bin/chage
# RHEL/AlmaLinux/Rocky
chmod u+s /usr/libexec/openssh/ssh-keysign
chmod u+s /usr/bin/chage
Откатывать ptrace_scope нужно в 1 (значение по умолчанию в Ubuntu), не в 0.
Шаг 3. Ротировать SSH host keys
Даже после установки патча — если атакующий уже получил ключи до обновления, старые ключи скомпрометированы. Пересоздать на критичных серверах:
rm /etc/ssh/ssh_host_*
ssh-keygen -A
systemctl restart ssh # Debian/Ubuntu
systemctl restart sshd # RHEL/AlmaLinux/Rocky
Предупредить пользователей: при следующем подключении SSH выдаст предупреждение о смене ключа — это нормально, нужно принять новый отпечаток.
Шаг 4. Аудит локальных учёток и смена паролей
Проверить кто вообще имеет shell-доступ на машине:
awk -F: '$7 !~ /nologin|false/ {print $1, $7}' /etc/passwd
Всё лишнее — заблокировать:
usermod -s /usr/sbin/nologin имя_пользователя
Если непривилегированные пользователи с shell-доступом на машине были — считать хеши паролей скомпрометированными и сменить:
passwd имя_пользователя
Шаг 5. Проверить NoNewPrivileges в критичных сервисах
NoNewPrivileges — директива systemd, которая запрещает процессу получать привилегии выше тех, с которыми он запущен. Даже если атакующий получил shell от имени www-data и попытается эксплуатировать похожий вектор — повышение привилегий будет заблокировано на уровне ядра.
Это не прямой фикс CVE-2026-46333, но снижает поверхность атаки для похожих race condition:
systemctl show nginx | grep NoNewPrivileges
systemctl show postgresql | grep NoNewPrivileges
Если пусто или false — добавить:
systemctl edit nginx
[Service]
NoNewPrivileges=yes
systemctl daemon-reload && systemctl restart nginx
То же самое для PostgreSQL, MySQL и других сервисов запущенных от непривилегированных пользователей.
ВЫВОД
CVE-2026-46333 — напоминание что «сервер снаружи закрыт» и «сервер безопасен» — разные утверждения. Локальный непривилегированный доступ, который кажется безобидным, превращается в кражу секретов которые живут дольше самой уязвимости.
Патч — сегодня. Ротация ключей — после патча. Аудит учёток — хороший повод сделать это в любом случае.
PoC публичный. Счёт идёт на часы.
