Статья Локальное повышение привилегий Linux через CVE-2026-46333: ptrace race condition от шелла до root

Плата одноплатного компьютера на чёрном антистатическом коврике со снятым экраном процессора. Тонкий щуп касается отладочного разъёма, у обгоревшей дорожки вьётся дымок.


В мае 2026-го Qualys Threat Research Unit раскрыла CVE-2026-46333 - логическую ошибку в __ptrace_may_access() ядра Linux. Баг прожил в mainline-коде девять лет, начиная с v4.10-rc1 (ноябрь 2016). Четыре рабочих эксплойта - против дефолтных инсталляций Debian 13, Ubuntu 24.04/26.04, Fedora 43 и 44. Непривилегированный локальный пользователь мог читать /etc/shadow, красть SSH host private keys или получить root-шелл через hijack D-Bus соединений к systemd. Публичные PoC появились почти сразу после раскрытия: по оценке CISA-ADP, технический импакт - total, статус эксплуатации - poc. Волна ядерных LPE-уязвимостей мая 2026 не ограничилась одним CVE: помимо CVE-2026-46333 были раскрыты PinTheft, DirtyClone (CVE-2026-43503, CVSS 8.8, CWE-664) и pedit COW (CVE-2026-46331, CWE-190/CWE-787). CVE-2026-31431 (не путать с CVE-2026-46333), с которой CVE-2026-46333 объединяется в цепочку эксплуатации, актуально эксплуатируется и внесена в CISA KEV (дедлайн патчинга - 2026-05-15; short description: "Linux Kernel Incorrect Resource Transfer Between Spheres Vulnerability").

Ситуация стандартная: ты уже на сервере. Получил шелл от www-data через RCE в веб-приложении, зашёл по SSH с угнанными low-priv credentials или попал на хост через скомпрометированный CI-раннер. Между тобой и полным контролем - барьер привилегий ядра. И вот тут начинается самое интересное.

По MITRE ATT&CK эксплуатация SUID/SGID-бинарей - это T1548.001 (Setuid and Setgid, тактика Privilege Escalation). В отличие от классических GNU/Linux privesc-техник - misconfigured sudo, writable cron (T1053.003), capabilities с cap_setuid - здесь не нужна misconfiguration на целевой системе. Уязвимость сидит в самом ядре. Дефолтная конфигурация дистрибутива - достаточное условие для эксплуатации. Никаких кривых sudoers, никаких забытых cron-скриптов. Просто ядро с багом, который девять лет никто не замечал.

Финансовый импакт: на shared hosting или в мультитенантных средах одна скомпрометированная учётка - путь к root на всём хосте. Украденные SSH host keys открывают on-path атаки на SSH-соединения, а хеши из /etc/shadow - offline-брутфорс паролей.

[Применимо: внутренний пентест, post-exploitation на Linux-хостах, grey box с low-priv credentials]

Анатомия CVE-2026-46333: логическая ошибка dumpable-проверки в ptrace ядра Linux​

1783690743404.webp

По NVD, CVSS 3.1 score - 7.1 (HIGH); вектор: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N. Разберём компоненты: AV:L - только локальный доступ; AC:L - низкая сложность (race condition воспроизводится стабильно); PR:L - нужен любой непривилегированный пользователь; C:H/I:H - полная компрометация конфиденциальности и целостности; A:N - доступность не затрагивается. Подробнее - в нашем руководстве по linux для пентестера.

Классификация: CWE-269 (Improper Privilege Management). Уязвимые ветки по OSV.dev: 4.10.0–5.10.255, 5.11.0–5.15.206, а также 5.16.0+ (проверьте свою ветку по OSV.dev). Red Hat подтвердила уязвимость для RHEL 8, 9, 10 и OpenShift Container Platform 4.

Механика: окно гонки при do_exit() и pidfd_getfd()​

Функция __ptrace_may_access() использует флаг dumpable, чтобы определить - можно ли присоединиться к чужому процессу через ptrace. Флаг концептуально привязан к наличию memory map (mm). А проблема - в логической ошибке проверки dumpable при transient-состоянии teardown (официальный CWE - CWE-269, Improper Privilege Management; эксплуатация имеет race-подобный характер, но первопричина - некорректная логика get_dumpable() при mm == NULL). Возникает при завершении привилегированного процесса.

Последовательность событий в do_exit() привилегированного SUID-бинаря:
  1. Процесс (например, ssh-keysign) выполняет работу с повышенными привилегиями, открывает /etc/ssh/*_key
  2. Процесс вызывает exit(), ядро начинает teardown
  3. exit_mm() освобождает memory map - указатель mm становится NULL
  4. Файловые дескрипторы и IPC-соединения ещё открыты - вот оно, окно гонки
  5. Только потом ядро закрывает open file descriptors
Между шагами 3 и 4 - самое вкусное. Функция __ptrace_may_access() видит mm == NULL и пропускает ветку if (mm && …) - проверка dumpable не выполняется. При дефолтном ptrace_scope=1 YAMA LSM разрешает доступ parent→child. В нормальных условиях ptrace_may_access() всё равно блокировал бы доступ к SUID-процессу из-за сброшенного dumpable-флага, но после exit_mm() эта проверка не срабатывает - оба барьера сняты одновременно. Атакующий вызывает pidfd_getfd() (syscall, добавленный в v5.6-rc1, январь 2020) и копирует открытые файловые дескрипторы привилегированного процесса к себе.

Ключевой момент: для SUID-бинарей ptrace_may_access() обычно блокирует доступ из-за смены euid и сброса dumpable-флага, но после exit_mm() проверка dumpable пропускается (mm == NULL), а YAMA при ptrace_scope=1 разрешает доступ parent->child. Два барьера падают одновременно.

Upstream fix - кеширование последнего состояния dumpability для процессов с mm == NULL и требование CAP_SYS_PTRACE для доступа к таким процессам.

Когда ptrace уязвимость Linux не работает​

Техника блокируется если:
  • kernel.yama.ptrace_scope равен 2 (только CAP_SYS_PTRACE) или 3 (полный запрет). YAMA LSM отклоняет pidfd_getfd() для непривилегированных пользователей
  • Ядро обновлено до 5.10.256+ или 5.15.207+
  • На хосте нет SUID/SGID-бинарей с доступом к чувствительным файлам (маловероятно для дефолтных инсталляций - такое бывает разве что на hardened-контейнерах)
  • Ядро старше v5.6 - syscall pidfd_getfd() отсутствует; PoC-примитив недоступен, хотя ядра 4.10–5.5 содержат уязвимый код
  • Контейнерные среды: по оценке Canonical, утечка ограничена контейнером и "generally unlikely to be useful to an attacker"

Четыре вектора эксплуатации CVE-2026-46333

Требования к окружению​

  • ОС: Linux с ядром 4.10 - 5.15.206 (не включая патченные ветки)
  • Привилегии: любой непривилегированный пользователь
  • ptrace_scope: дефолтное значение 1 (проверить: sysctl kernel.yama.ptrace_scope)
  • pidfd_getfd: ядро 5.6+ (есть в большинстве дистрибутивов с 2020 года)
  • SUID/SGID бинари: /usr/bin/chage, /usr/lib/openssh/ssh-keysign, /usr/bin/pkexec
  • RAM: от 512 МБ (эксплойт нетребователен)
  • Сеть: не требуется (полностью локальная атака)
Bash:
uname -r                                # версия ядра
sysctl kernel.yama.ptrace_scope          # должно быть 1 для эксплуатации
find / -type f \( -perm -4000 -o -perm -2000 \) 2>/dev/null | head -20  # SUID/SGID бинари

Утечка credentials: /etc/shadow и SSH host keys​

chage -- /etc/shadow. На Debian/Ubuntu chage по умолчанию стоит с set-gid-shadow. Вызываешь chage -l root - она открывает /etc/shadow, потом завершается. В окне между exit_mm() и закрытием дескрипторов атакующий через pidfd_getfd() перехватывает FD и читает содержимое - хеши паролей всех локальных пользователей. Canonical оговаривается, что алгоритмы хеширования Ubuntu (SHA-512/yescrypt) стойки к offline-брутфорсу при сильных паролях. Но "сильные пароли" на shared hosting - скорее исключение, чем правило. Уязвимость воспроизводится на современных дистрибутивах с ядрами 4.10–5.15.206.

ssh-keysign -- SSH host private keys. SUID-бинарь /usr/lib/openssh/ssh-keysign при запуске открывает /etc/ssh/*_key. Атакующий перехватывает дескриптор - и вот у него приватные ключи хоста. Импакт: on-path атаки (MitM) на SSH-соединения, компрометация host-based authentication. Публичный PoC-репозиторий KaraZajac/CHARON содержит pre-built эксплойт: порождает дочерний процесс, открывает pidfd и многократно сканирует файловые дескрипторы через pidfd_getfd(), сверяя каждый дуплицированный дескриптор с целевым файлом через /proc/self/fd/<n>.

pkexec и accounts-daemon: произвольный код от root​

Два продвинутых вектора дают не чтение файлов, а полноценное выполнение команд от root. Тут уже не просто утечка - это полный захват.

pkexec (set-uid-root): атакующий перехватывает аутентифицированное D-Bus соединение к systemd. Предусловие - наличие allow_active сессии на консоли, что работает даже при удалённом SSH-подключении, если кто-то залогинен локально. Применимо к дистрибутивам с ядрами 4.10–5.15.206 и дефолтным ptrace_scope=1.

accounts-daemon (root daemon): эксплойт ищет PID accounts-daemon через /proc, триггерит активность через busctl и AccountsService, затем гоночными потоками вызывает pidfd_getfd() для кражи D-Bus сокета. Через украденный сокет отправляются handcrafted D-Bus-сообщения: SetShell, SetAccountType(admin), SetPassword с контролируемым хешем. Результат - текущий пользователь получает административные права. Красиво, если честно. Применимо к дистрибутивам с уязвимыми ядрами и дефолтным ptrace_scope=1.

Разбор PoC: как работает локальный эксплойт Linux​

1783690830761.webp

Публичный репозиторий KaraZajac/CHARON (GitHub, обновлён 2026-05-21, 6 звёзд) содержит pre-built PoC для CVE-2026-46333 (Linux ptrace mm==NULL fd theft). Алгоритм работы:
  1. Атакующий вызывает целевой SUID/SGID-бинарь как дочерний процесс, перенаправляя stdout/stderr в /dev/null
  2. Немедленно открывает pidfd через syscall pidfd_open()
  3. В цикле (сотни итераций, десятки раундов) вызывает pidfd_getfd(pidfd, target_fd, 0) для диапазона файловых дескрипторов (3-63 для ssh-keysign, 3-499 для research-версий)
  4. Большинство вызовов возвращают -EPERM - процесс жив, dumpable-проверка работает штатно
  5. В момент, когда дочерний процесс входит в do_exit() и exit_mm() обнуляет mm, pidfd_getfd() проходит - дескриптор дублируется
  6. Атакующий проверяет путь через /proc/self/fd/<n>, при совпадении с целевым файлом - читает содержимое
Расширенные реализации PoC увеличивают число итераций и диапазон сканируемых дескрипторов для повышения надёжности.

Primitive стабилен: окно между exit_mm() и закрытием FD достаточно широкое для многократных probe-попыток. AC:L в CVSS-векторе - не для красоты, race действительно воспроизводится с первых попыток.

EPSS - 0.9627 (перцентиль 99.87, Top 1%), экстремально высокая вероятность эксплуатации. CISA-ADP SSVC decision: Act - патчить немедленно.

Механика цепочки:
  1. Через полученный примитив предположительно модифицируется /etc/shadow - хеш пароля root заменяется на контролируемый
  2. Атакующий выполняет su root с новым паролем
Примечание: точная механика цепочки описана по анализу стороннего PoC; NVD-описание CVE-2026-31431 указывает на revert к out-of-place операции в algif_aead и не подтверждает примитив произвольной записи в page cache. Каждая уязвимость по отдельности серьёзна; детали цепочки требуют независимой верификации.

Обнаружение и защита от race condition exploit Linux​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

; D3-PLA (Process Lineage Analysis) - выявление аномальных parent-child цепочек.

Чеклист для blue team:
  1. Проверить ptrace_scope: sysctl kernel.yama.ptrace_scope (если 1 - уязвимо)
  2. Установить kernel-обновления и перезагрузить (RHEL 8/9/10: все фиксы выпущены, Ubuntu: sudo apt update && sudo apt upgrade)
  3. Если патчинг невозможен - применить ptrace_scope=2
  4. На хостах с untrusted users за период экспозиции - ротировать SSH host keys, проверить /etc/shadow
  5. Добавить auditd-правило: auditctl -a always,exit -F arch=b64 -S pidfd_getfd -F auid>=1000 -F auid!=-1 -k ptrace_race (добавьте аналогичное правило для arch=b32; без фильтра по auid ожидайте шум от контейнерных runtime и systemd)
  6. Проверить cron, SUID и capabilities на предмет смежных privesc-векторов (LinPEAS, pspy)
Девять лет. Столько этот баг прожил в mainline-ядре. Не в экзотическом модуле, а в ptrace_may_access() - функции, которую ревьювили десятки разработчиков. Ошибка не в сложной математике, а в порядке операций при teardown: exit_mm() вызывается раньше закрытия файловых дескрипторов. Этого достаточно.

CVE-2026-46333 меняет отношение к ptrace в production. Значение ptrace_scope=1 де-факто небезопасно и должно уйти из дефолтных конфигураций. Ubuntu уже двигается в эту сторону, но большинство RHEL-based дистрибутивов всё ещё шипят с единицей. На каждом пентесте GNU/Linux-инфраструктуры с мая 2026 проверяю ptrace_scope первым делом - до sudo, до SUID, до cron. Если единица - считай, полдела сделано. Отдельная история - цепочка с CVE-2026-31431 (CISA KEV, EPSS 96.27%): это уже не теоретический risk, а подтверждённая боевая эксплуатация. Для defensive-команд вывод один: kernel-обновления в окно обслуживания больше ждать не могут. На HackerLab (https://hackerlab.pro) лежит сценарий, где этот primitive нужно собрать в полную цепочку.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab