19 лет в тени: CIFSwitch даёт root через механизм монтирования SMB-шар.
Представьте: на вашем сервере работает обычный непривилегированный пользователь — разработчик, арендатор VPS, студент на shared-хостинге. Он запускает одну команду. Через секунду у него root. SELinux при этом может быть включён в enforcing-режиме. AppArmor — активен. Ничего не сработало.
Именно так работает CIFSwitch — уязвимость в Linux-ядре, которую исследователь Asim Viladi Oglu Manizada обнаружил и раскрыл 28 мая 2026 года. CVE-идентификатор на момент публикации ещё не присвоен, патч для ядра уже больше недели как готов и стоит в очереди для стабильных веток. Публичный proof-of-concept вышел одновременно с disclosure — то есть любой желающий может воспроизвести атаку прямо сейчас.
Баг сидит в коде с 2007 года. 19 лет. Он прошёл через сотни аудитов, тысячи обновлений и дожил до 2026-го совершенно незамеченным — потому что это не переполнение буфера и не use-after-free. Это логическая ошибка на стыке ядра и userspace, и обнаружить её можно было только если смотреть на систему целиком, а не на отдельные компоненты.
ЧТО ТАКОЕ CIFS И cifs.upcall
CIFS (Common Internet File System) — это реализация SMB-протокола в Linux-ядре. Грубо говоря, это то, что позволяет Linux монтировать сетевые папки Windows-серверов командой mount -t cifs. Модуль cifs в ядре занимается всем файловым уровнем: монтированием, чтением, записью, переговорами с сервером. Но есть нюанс — Kerberos-аутентификацию ядро не берёт на себя. Для этого существует отдельная схема.
Когда CIFS-клиенту нужен Kerberos/SPNEGO-токен для аутентифицированного монтирования, он использует механизм Linux keyrings: запрашивает ключ типа cifs.spnego. Система видит этот запрос и по правилу в /etc/request-key.d/cifs.spnego.conf запускает userspace-помощника — cifs.upcall — с правами root. Тот получает Kerberos-материал и возвращает его ядру. Вся эта схема — часть пакета cifs-utils.
Ключевое здесь — cifs.upcall запускается с правами root. Это не баг сам по себе, это архитектурное решение: помощнику нужны привилегии чтобы работать с credential-кэшами разных пользователей. Проблема в том, что ядро не проверяло: а кто, собственно, попросил запустить этого помощника?
КАК РАБОТАЕТ БАГ
Когда ядро запрашивает ключ cifs.spnego, оно формирует строку-описание из реального состояния системы: хост, uid, creduid, pid процесса, тип upcall-target. Примерно вот так: ver=0x2;host=fs.acme.com;ip4=192.168.1.10;sec=krb5;uid=0x3e8;creduid=0x3e8;pid=0x4f2a;upcall_target=app. Эти поля реальны — ядро берёт их из своего состояния.
Проблема в определении типа ключа cifs_spnego_key_type в файле fs/smb/client/cifs_spnego.c. До патча структура выглядела так:
struct key_type cifs_spnego_key_type = {
.name = "cifs.spnego",
.instantiate = cifs_spnego_key_instantiate,
.destroy = cifs_spnego_key_destroy,
.describe = user_describe,
};
Видите что отсутствует? Поле .vet_description — хук, который должен проверять легитимность описания ключа. Без него ядро не отличало запрос от CIFS-клиента от запроса любого другого процесса. Обычный непривилегированный пользователь мог вызвать request_key("cifs.spnego", полностью_поддельное_описание, "") — и система реагировала точно так же, как на запрос от ядра: запускала cifs.upcall с правами root.
Важная деталь: ядру даже не нужно было успешно вернуть ключ. cifs.upcall запускался до того, как ядро выдавало ошибку -ENOKEY. Атака срабатывала раньше отказа.
КАК ИМЕННО ЭКСПЛУАТИРУЕТСЯ
Атакующий формирует поддельную строку описания, в которой устанавливает upcall_target=app и прописывает pid своего процесса. Дальше — цепочка доверия начинает работать против системы.
cifs.upcall, запущенный как root, парсит описание и видит upcall_target=app. По коду это сигнал: «нужно перейти в namespace приложения, чтобы корректно работать в контейнерах». Хелпер вызывает switch_to_process_ns(arg->pid) — и переходит в namespace процесса, указанного атакующим. Этот namespace атакующий контролирует полностью.
Теперь root-процесс находится в пространстве имён атакующего. Следующий шаг — getpwuid(uid): хелпер делает lookup учётной записи через NSS (Name Service Switch) чтобы получить gid пользователя. NSS читает конфигурацию из /etc/nsswitch.conf — которую атакующий в своём namespace уже подменил. Подменённый nsswitch.conf указывает на libnss_pwn.so.2 — библиотеку атакующего. Root-хелпер её загружает. Библиотека выполняется с правами root.
В демонстрационном PoC Manizada эта библиотека записывает правило в /etc/sudoers.d — и атакующий получает возможность выполнять любые команды как root через sudo.
ЧТО ПРОИСХОДИТ ДАЛЬШЕ — ЗАВИСИТ ОТ НАСТРОЕК СИСТЕМЫ
CIFSwitch — не универсальная уязвимость. Для успешной эксплуатации нужно одновременно выполнение четырёх условий: уязвимое ядро (код присутствует с 2007 года), установленный cifs-utils версии 6.14+ с правилом cifs.spnego, разрешённые непривилегированные user namespaces, и LSM-политика которая не блокирует цепочку.
Здесь начинается интересное. SELinux в режиме enforcing — это звучит как надёжная защита. Но тестирование показало: CentOS Stream 9 GNOME, Rocky Linux 9 Workstation и AlmaLinux 9.7 эксплуатируются с включённым SELinux enforcing. AppArmor тоже не помогает на Linux Mint 21.3/22.3 и SLES 15 SP7. Политики по умолчанию просто не покрывают этот вектор атаки.
Защищают только более строгие конфигурации: Fedora 40–44, CentOS Stream 10 и Rocky Linux 10 не эксплуатируются — но только потому что SELinux там настроен жёстче. Стоит выполнить setenforce 0 — и защита исчезает. Ubuntu 26.04 блокирует атаку через AppArmor userns policy. На 24.04 политика тоже активна, но есть обходной путь: aa-exec -p trinity запускает процесс под профилем AppArmor который разрешает создание user namespaces — и атака снова работает. Amazon Linux 2 неуязвим — но только потому что там слишком старый cifs-utils 6.2 без поддержки namespace-переключения.
ЦЕПОЧКА АТАКИ В РЕАЛЬНОМ МИРЕ
Сценарий: арендатор VPS на Ubuntu 22.04 с установленным cifs-utils. Сервер используется как часть shared-инфраструктуры — несколько клиентов, у каждого свой непривилегированный пользователь. Один из арендаторов скачивает PoC с GitHub и запускает одну команду. Через секунду у него root-доступ ко всему серверу — включая данные остальных арендаторов, базы данных, SSL-сертификаты, SSH-ключи.
Для этого не нужно знать пароль root. Не нужен сетевой доступ. Не нужно эксплуатировать память — никакого heap spray, никаких ROP-цепочек. Просто один системный вызов request_key() с поддельным описанием, и дальше логика системы делает всё сама.
Вектор входа может быть любым: SSH-доступ с ограниченными правами, web shell через уязвимость в веб-приложении, компрометация сервисного аккаунта, escape из контейнера который не изолирует user namespaces. Главное — непривилегированный пользователь на уязвимой системе.
ХРОНОЛОГИЯ
2007 год — баг появляется в коде. В fs/smb/client/cifs_spnego.c определяется структура cifs_spnego_key_type без хука .vet_description. Это не ошибка реализации — это пропущенная проверка при проектировании механизма.
2026, май 16 — Asim Viladi Oglu Manizada сообщает о проблеме мейнтейнерам ядра и cifs-utils.
2026, май 16–27 — патч для ядра (commit 3da1fdf4efbc) готов и поставлен в очередь для стабильных веток. Идёт скоординированное embargo с linux-distros@ — чтобы дистрибутивы успели подготовить пакеты до публичного раскрытия.
2026, май 28 — embargo истекает. Manizada публикует полный writeup на heyitsas.im и PoC на GitHub одновременно с disclosure в oss-security mailing list. К этому моменту патч для ядра уже больше недели в очереди stable, но обновлённые пакеты дистрибутивов ещё выкатываются.
2026, июнь 1 — патчи продолжают выходить для Red Hat, Ubuntu, Debian, SUSE, Oracle, Amazon Linux. CVE-идентификатор ещё не присвоен.
КАК ЭТО МОГЛО СУЩЕСТВОВАТЬ 19 ЛЕТ
Память программиста не держит весь граф доверия системы одновременно. Когда в 2007 году реализовывали механизм upcall для CIFS, разработчик видел свой кусок: ядро запрашивает ключ, userspace-хелпер его получает и выполняет работу. Логика правильная. Что не было очевидным — это что любой процесс может инициировать тот же запрос с поддельными данными, потому что key type не проверяет происхождение описания.
Это классический паттерн confusion attack: компонент A доверяет данным, которые, по его предположению, могут прийти только от компонента B. Но нет механизма который это гарантировал бы. Найти такую уязвимость при ручном аудите крайне сложно — нужно одновременно держать в голове и ядерный код, и userspace-хелпер, и механизм keyrings, и то как NSS работает в нестандартных namespace-условиях. Ни один аудитор не смотрит на весь этот граф сразу.
К тому же условия эксплуатации нетривиальные: нужен именно cifs-utils определённой версии, именно с поддержкой namespace-переключения, именно без блокирующей LSM-политики. На серверах без cifs-utils баг вообще не эксплуатируется. Это резко сужало шанс случайно наткнуться на проблему в процессе нормальной работы.
ПОЧЕМУ НАХОДЯТ СЕЙЧАС
CIFSwitch нашёл не человек. Точнее — нашёл человек, но с помощью инструмента который позволил увидеть именно этот тип связей между компонентами.
Manizada описывает подход в своём writeup: он оснастил LLM-агентов инструментом обхода графов, вдохновлённым исследованием GraphWalk. Агенты строили семантический граф безопасно-значимых объектов — кто создаёт объект, кто его потребляет как авторитетный источник, где происходит разрыв между тем, что объект утверждает о себе, и тем, что потребитель предполагает. На достаточно большом графе модели с таким инструментом превосходили reasoning-модели без него.
Это важный сигнал для всей отрасли: класс уязвимостей типа «confusion across trust boundaries» — именно то, что LLM-assisted анализ находит лучше людей. Человек-аудитор читает код линейно; граф-трейсер видит связи через десятки файлов и компонентов. CIFSwitch — не последний баг такого рода, найденный таким способом.
ПОЧЕМУ ОПАСНО
Масштаб охвата поражает. Manizada протестировал порядка 30 комбинаций дистрибутивов и версий. Уязвимы в дефолтной конфигурации (без каких-либо изменений): Linux Mint 21.3 и 22.3, CentOS Stream 9 GNOME, Rocky Linux 9 Workstation, все версии Kali Linux начиная с 2021.4 вплоть до 2026.1 в headless-варианте, AlmaLinux 9.7, SLES 15 SP7. Это не экзотика — это мейнстримные дистрибутивы которые стоят на рабочих серверах по всему миру.
Отдельная история — облачные образы. AlmaLinux 9.7 Azure cloud image уязвим прямо из коробки — cifs-utils там предустановлен. Amazon Linux 2023 уязвим при SELinux permissive, но только если cifs-utils установлен вручную — по умолчанию его там нет. Это означает что среди инстансов в облаке, запущенных с уязвимыми образами, часть уязвима прямо сейчас — если администратор не успел обновить ядро.
Для сисадмина который управляет shared-хостингом или VPS-платформой ситуация особенно острая: один скомпрометированный арендатор с shell-доступом — это полный контроль над хостовой системой и данными всех остальных. Для enterprise-среды с множеством разработчиков или сервисных аккаунтов — любой из них потенциальный вектор эскалации привилегий до root.
КАК НЕ ДОПУСТИТЬ ПОВТОРЕНИЯ
CIFSwitch — не уникальный случай. CopyFail (CVE-2026-31431) несколькими месяцами ранее использовал тот же принцип: привилегированный компонент доверял данным, происхождение которых не проверялось. Класс атак «confusion across trust boundaries» — логические баги на стыке компонентов с разными уровнями привилегий — встречается регулярно и крайне сложен для ручного аудита.
Архитектурный ответ — принцип наименьшего доверия: привилегированный компонент не должен принимать данные от менее привилегированного без явной верификации источника. В случае с ядром это означает .vet_description в key_type. В случае с userspace-хелперами — не доверять полям из key description как kernel-originated без проверки.
На уровне конфигурации серверов: непривилегированные user namespaces — это функция которую большинство продакшн-серверов не используют, но которая присутствует по умолчанию. Её отключение одной строкой в sysctl убирает целый класс LPE-атак, не только CIFSwitch. Если cifs-utils не нужен — его удаление полностью ликвидирует attack surface для этой уязвимости.
КОМАНДЫ ИСПРАВЛЕНИЯ
Первым делом — проверить, установлен ли cifs-utils на системе:
dpkg -l cifs-utils 2>/dev/null || rpm -q cifs-utils 2>/dev/null
Если пакет не установлен — система не эксплуатируется по этому вектору независимо от версии ядра, дальнейшие шаги не нужны. Если установлен — читайте дальше.
Если cifs-utils не нужен — самое простое и надёжное решение прямо сейчас:
# Debian / Ubuntu
sudo apt remove cifs-utils
# RHEL / CentOS / Rocky / AlmaLinux
sudo dnf remove cifs-utils
Если cifs-utils нужен, но Kerberos-аутентификация для SMB не используется — заблокировать правило request-key. Следующая команда из writeup Manizada переопределяет правило так, что запрос ключа немедленно отклоняется вместо запуска хелпера:
cat >/etc/request-key.d/cifs.spnego.conf <<'EOF'
create cifs.spnego * * /usr/sbin/keyctl negate %k 30 %S
EOF
Если SMB-монтирование не используется вовсе — заблокировать загрузку модуля cifs:
echo "install cifs /bin/true" | sudo tee /etc/modprobe.d/disable-cifs.conf
Отдельно стоит отключить непривилегированные user namespaces — это блокирует не только CIFSwitch, но и целый класс похожих LPE. Если сервер не использует контейнеры или функции которым нужны user namespaces:
# Debian / Ubuntu — нужны оба параметра
sudo sysctl -w kernel.unprivileged_userns_clone=0
sudo sysctl -w user.max_user_namespaces=0
printf "kernel.unprivileged_userns_clone=0\nuser.max_user_namespaces=0\n" | sudo tee -a /etc/sysctl.d/99-hardening.conf
# RHEL / CentOS / Rocky / AlmaLinux
sudo sysctl -w user.max_user_namespaces=0
echo "user.max_user_namespaces=0" | sudo tee -a /etc/sysctl.d/99-hardening.conf
Проверить что любая из мер сработала можно с помощью официального PoC. После применения митигации запустите:
git clone https://github.com/manizada/CIFSwitch
cd CIFSwitch && make && ./cifswitch
Если PoC завершается без получения root — митигация работает. Это официальный способ валидации из writeup автора.
ОБНОВЛЕНИЕ
Главное долгосрочное решение — обновить ядро. Upstream-фикс — commit 3da1fdf4efbc490041eb4f836bf596201203f8f2: добавляет функцию cifs_spnego_key_vet_description(), которая разрешает создание ключа только когда запрос пришёл от CIFS через spnego_cred. Дистрибутивы бэкпортируют этот фикс — следите за security-каналом и обновляйтесь как только пакет появится:
# Debian / Ubuntu
sudo apt update && sudo apt upgrade
# RHEL / CentOS / Rocky / AlmaLinux
sudo dnf update kernel
После обновления — перезагрузка обязательна, новое ядро вступает в силу только после reboot. Убедиться что система загрузилась с обновлённым ядром:
uname -r
Вывод покажет версию ядра — сравните с версией патча в security-бюллетене вашего дистрибутива. Финальная проверка — запустить официальный PoC: если он завершается без получения root, ядро содержит фикс:
git clone https://github.com/manizada/CIFSwitch
cd CIFSwitch && make && ./cifswitch
До выхода патча для вашего дистрибутива используйте меры из раздела выше.
ВЫВОДЫ
CIFSwitch — это напоминание о том, что «безопасный» не значит «проверенный». Ядро Linux проходит через тысячи аудитов и review, SELinux и AppArmor работают на миллионах серверов — и всё равно 19-летний баг на стыке двух компонентов дожил до 2026 года. Не потому что никто не смотрел, а потому что никто не смотрел на обоих одновременно.
Если вы управляете серверами на Linux — первое действие сегодня: проверить есть ли cifs-utils на тех машинах где его не должно быть, и удалить. Второе: отключить непривилегированные user namespaces на серверах где они не нужны. Третье: следить за security-каналом дистрибутива и обновить ядро как только патч выйдет.
Для тех кто управляет VPS-платформами и shared-хостингом — это не просто рекомендация. PoC публичный, он работает, и любой арендатор с shell-доступом может попробовать его прямо сейчас. Время на реакцию — не дни, а часы.
