
Блокировка цепочки DirtyFrag Linux LPE (CVE-2026-43284 / CVE-2026-43500) во время выполнения с помощью Cilium Tetragon TracingPolicy
Блокировка цепочки повышения привилегий DirtyFrag в Linux (CVE-2026-43284 / CVE-2026-43500) во время выполнения с помощью TracingPolicy от Cilium Tetragon. Политика отправляет SIGKILL публичному proof-of-concept на этапе настройки сокета, до того, как он достигнет записи в page-cache, которая дала бы ему root.
Что это такое. Лабораторная заметка. Я впервые настроил Tetragon и хотел проверить, сможет ли он действительно остановить реальную, актуальную LPE-уязвимость ядра. Смог. В этом документе описано, что именно я запускал, что сработало и, что не менее важно, пределы того, что это доказывает. Это не замена исправлению.
Файлы: этот README (самодостаточный, доказательства встроены) · block-dirtyfrag.yaml (политика)
| Эксплойт | DirtyFrag — публичный PoC: V4bel/dirtyfrag |
| CVE | CVE-2026-43284 (запись в page-cache xfrm-ESP), CVE-2026-43500 (запись в page-cache RxRPC) |
| Хост | Ubuntu 24.04.4 LTS, Tetragon v1.7.0 (автономный, systemd) |
Базовый уровень — без политики, ядро 6.8.0-88 (уязвимое) | PoC → uid=0(root) |
С политикой — ядро 6.8.0-88 | PoC получает SIGKILL при socket(AF_RXRPC); пользователь остаётся uid=1000 |
Исправленное ядро 6.8.0-134 | PoC самостоятельно терпит неудачу (rc=4); хук сокета всё равно срабатывает, обнаружение, а не устранение (примечание) |
| Характер контроля | Компенсирующий контроль / виртуальный патч, не исправление ядра |
DirtyFrag — это цепочка локального повышения привилегий, построенная на двух независимых примитивах записи в page-cache ядра Linux: один в пути расшифровки на месте xfrm/ESP (IPsec) (CVE-2026-43284), а другой — в пути RxRPC (CVE-2026-43500). Каждый из них позволяет непривилегированному локальному пользователю записывать контролируемые атакующим байты в страницы page-cache, доступные только для чтения, например, в кэшированный образ setuid-root-бинария, такого как /bin/su, и оттуда получить root. Запись происходит только в памяти; файл на диске никогда не изменяется, поэтому мониторинг целостности файлов ничего не видит. Это тот же класс ошибок, что и Dirty Pipe и Copy Fail. Два CVE намеренно объединены в цепочку: если один путь недоступен в данной среде, работает другой.
На этом хосте непривилегированные пользовательские пространства имён ограничены AppArmor (kernel.apparmor_restrict_unprivileged_userns = 1), что блокирует половину цепочки, связанную с ESP. Остаётся путь RxRPC, который открывает сокет AF_RXRPC (семейство адресов 33) — и именно этот шаг убивает данная политика.
Настоящее решение — исправленное ядро. Всё здесь — временная мера для хоста, который вы пока не можете пропатчить. Ubuntu выпустила исправления DirtyFrag некоторое время до этого теста, так что это известная N-day, а не живой 0-day — суть в том, чтобы показать, что может сделать контроль времени выполнения на хосте, который по какой-то причине всё ещё работает на уязвимом ядре.
Полная политика: block-dirtyfrag.yaml. Она устанавливает два kprobe.
Хук 1 — сокеты для подготовки (sys_socket). Основной контроль. Путь RxRPC эксплойта должен вызывать socket(AF_RXRPC, …) при каждом запуске, независимо от того, загружен ли уже какой-либо модуль ядра. Политика срабатывает по семейству адресов:
AF_RXRPC) → Sigkill. На любом хосте, который не является клиентом AFS, практически ничто легитимное не открывает сокет AF_RXRPC, поэтому тотальное убийство здесь безопасно и с высокой степенью уверенности.AF_ALG) → Post (только аудит, без убийства). AF_ALG — это пользовательский API криптографии ядра, и у него есть легитимные пользователи (cryptsetup, инструментарий libkcapi, некоторые FIPS-процессы). Убийство только по семейству вызвало бы ложные срабатывания, поэтому здесь происходит только логирование. Путь к принуждению: аудит в течение некоторого времени, создание белого списка NotIn из наблюдаемых бинарников, затем рассмотрение возможности повышения до Sigkill.Хук 2 — автозагрузка уязвимого модуля (security_kernel_module_request). Защита в глубину. Срабатывает, когда ядро запрашивает автоматическую загрузку семейства уязвимых модулей (esp4, esp6, rxrpc, псевдонимы сокетов net-pf-33/net-pf-38, криптошаблоны pcbc/fcrypt). Ограничение: он срабатывает, только если модуль еще не загружен — после первого запуска эксплойта в рамках загрузки эти модули загружаются, и хук замолкает. Это уровень холодной загрузки, а не основной контроль.
Вместе: Хук 1 ловит эксплойт независимо от того, прогреты модули или нет; Хук 2 добавляет более раннее, более специфическое срабатывание на холодном хосте.
6.8.0-88-generic, которое уязвимо (базовый уровень достигает root). На машине также установлено 6.8.0-134-generic — исправленное ядро, и это было подтверждено напрямую: перезагрузка в -134 заставляет PoC самостоятельно терпеть неудачу (rc=4, без root), с политикой или без неё (см. примечание ниже). Чтобы воспроизвести демонстрацию смягчения, загрузите 6.8.0-88 через GRUB (он всё ещё установлен) — на любом исправленном ядре нечего смягчать.AF_ALG.lockdown,capability,landlock,yama,apparmor — нет bpf). Поэтому принуждение использует Sigkill от kprobe, а не внутриядерный запрет LSM. Убийство происходит на системном вызове socket() — до примитива записи — так что процесс завершается на обязательном раннем этапе, а не «блокирует саму уязвимость».Шаги 1–5 выполняются на одной и той же загрузке 6.8.0-88 (уязвимое ядро).
1. Окружение — Ubuntu 24.04.4, ядро 6.8.0-88-generic, Tetragon v1.7.0 активен под systemd, непривилегированные пользовательские пространства имён ограничены.
$ uname -r
6.8.0-88-generic
$ tetra version
CLI version: v1.7.0
$ systemctl is-active tetragon
active
2. Предусловия (политика ВЫКЛ) — чистая отправная точка: уязвимые модули не загружены, состояние xfrm отсутствует, политика не загружена.
$ lsmod | grep -E 'esp4|esp6|rxrpc' || echo "(none loaded)"
(none loaded)
$ sudo ip xfrm state # (пусто)
$ sudo tetra tp list
ID NAME STATE FILTERID NAMESPACE SENSORS KERNELMEMORY MODE NPOST NENFORCE NMONITOR
3. Базовый уровень (политика ВЫКЛ) — эксплойт работает. Это доказывает, что ядро действительно уязвимо; вся заметка на этом держится.
$ ./exp ; id
uid=0(root) gid=0(root) groups=0(root)
4. Загрузка политики.
$ sudo tetra tp add /etc/tetragon/tetragon.tp.d/block-dirtyfrag.yaml
tracing policy "…/block-dirtyfrag.yaml" added
5. Принуждение (политика ВКЛ) — убийство. Эксплойт завершается сигналом при вызове socket() и никогда не достигает su:
🚀 process /home/…/dirtyfrag/exp
❓ syscall /home/…/dirtyfrag/exp __x64_sys_socket
Kernel:
0x0: __x64_sys_socket+0x5
0x0: do_syscall_64+0x7f
0x0: entry_SYSCALL_64_after_hwframe+0x78
💥 exit /home/…/dirtyfrag/exp SIGKILL
Структурированное событие подтверждает и совпадение, и убийство — process_kprobe на системном вызове сокета, затем process_exit по сигналу для того же PID:
// process_kprobe — совпадение по AF_RXRPC
{ "process_kprobe": {
"process": { "pid": 24083, "uid": 1000, "binary": ".../dirtyfrag/exp" },
"function_name": "__x64_sys_socket",
"args": [ { "int_arg": 33, "label": "family" } ],
"policy_name": "block-dirtyfrag",
"action": "KPROBE_ACTION_POST"
} }
// process_exit — тот же PID, убит сигналом
{ "process_exit": {
"process": { "pid": 24083, "binary": ".../dirtyfrag/exp" },
"signal": "SIGKILL"
} }
action kprobe читается как KPROBE_ACTION_POST, потому что действие Post — это то, что генерирует видимое событие; действие Sigkill — это то, что приводит к отдельному выходу SIGKILL. Два события вместе являются доказательством: совпадение и убийство.
Примечание: в сыром захвате
07также содержится строкаmessageиз более ранней версии политики (до того, как частьAF_ALGбыла выделена в режим только аудита). Совпадение поfamily: 33и последующийSIGKILLидентичны во всех версиях; отличается только этот текст.
После выполнения на уязвимом ядре хост был перезагружен в 6.8.0-134-generic (исправленное ядро Ubuntu) и PoC был запущен снова:
$ ./exp # политика ВЫКЛ
dirtyfrag: failed (rc=4) # эксплойт самостоятельно терпит неудачу — нет root
$ ./exp # политика ВКЛ
Killed # SIGKILL при socket(AF_RXRPC)
Будем точны в том, что это показывает:
rc=4, без root), потому что ядро исправлено. Политике нечего предотвращать. Сравнение «до/после», несущее утверждение о смягчении, существует только на уязвимом ядре -88.socket(AF_RXRPC), поэтому Tetragon по-прежнему отправляет ему SIGKILL и регистрирует попытку в журнале, как на исправленном ядре, так и на неисправленном. Это имеет ценность как обнаружение попытки и как защита в глубину, но это не то же самое, что остановка работающего эксплойта.AF_RXRPC легитимен, и тотальное убийство сломает его. Проверьте на каждом узле.AF_ALG по замыслу работает только в режиме аудита. Вариант, использующий только путь AF_ALG с уже загруженными модулями, будет залогирован, но не убит, пока вы не переведёте эту часть в режим принуждения (после создания белого списка).AF_RXRPC открывается до примитива записи. Такой порядок делает раннее убийство эффективным; это не гарантия для любого возможного эксплойта.Настройка: Tetragon v1.7.0 автономный, запущен через systemctl; политики загружены с помощью tetra tracingpolicy add.