Блог

19 лет в тени: CIFSwitch даёт root через механизм монтирования SMB-шар.

CIFSwitch
CVE / Linux / Безопасность

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-доступом может попробовать его прямо сейчас. Время на реакцию — не дни, а часы.

Leave your thought here

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Поддержать автора

Если вам понравилась статья — вы можете поддержать автора.

Crypto donation button by NOWPayments