
Отчёт о Dirty Frag — цепочке уязвимостей локального повышения привилегий (LPE) в Linux, которая позволяет непривилегированному пользователю получить root-доступ.
Лабораторная работа по воспроизведению и обнаружению эксплойта для локального повышения привилегий в ядре Linux.
Статус: ПОДТВЕРЖДЕНО. Я выполнил воспроизведение, проверку без изменений на диске и обнаружение на уровне системных вызовов в лабораторной среде (ядро 6.18.9+kali-amd64). Этот документ — журнал лабораторной работы. Каждое утверждение ниже было зафиксировано в ходе воспроизведения. Скриншоты и артефакты — реальные снимки из виртуальной машины.
Dirty Frag объединяет две детерминированные логические ошибки в ядре Linux. Эти ошибки позволяют непривилегированному локальному пользователю перезаписать страничный кэш файлов, доступных только для чтения (например, /usr/bin/su), и получить root-оболочку:
Оба варианта используют тот же базовый шаблон, что и Dirty Pipe и Copy Fail. Системный вызов splice(2) помещает ссылку на страницу страничного кэша файла в слот frag в sk_buff на стороне отправителя. Атакующий может только читать этот файл. Затем код на принимающей стороне выполняет криптографическую операцию STORE на месте поверх этого frag. Это изменяет страничный кэш в оперативной памяти. Записи на диск не происходит, поэтому системы мониторинга целостности файлов (AIDE, Tripwire) не могут это обнаружить. Атака детерминирована. В ней нет гонки и нет паники ядра при сбое.
cac2661c53f3 (01.2017) до f4c50a4034e6 (исправлено 05.05.2026)2dc334f1a63a (06.2023) до aa54b1d27fe0 (исправлено 10.05.2026)CVE-2026-43284 = 8.8 (высокая), CVE-2026-43500 = 7.8 (высокая)Dirty Frag — это LPE без изменений на диске. Он повреждает страничный кэш в памяти, а не файл на диске. Традиционный мониторинг целостности файлов не может этого увидеть. Обнаружение должно происходить на уровне системных вызовов. Цепочка использует следующие примитивы системных вызовов: socket(AF_ALG)/socket(AF_RXRPC), splice и unshare(CLONE_NEWUSER|CLONE_NEWNET). Путь ESP также создаёт UDP-сокеты AF_INET и netlink-сокеты. Этот уровень является основным фокусом инженерии обнаружения в данном репозитории.
Оба варианта используют одно и то же слабое место: криптографию на месте, которая выполняет STORE байтов на страницу страничного кэша, которую атакующий размещает с помощью splice(2).
UDP_ENCAP_ESPINUDP.vmsplice, затем 16 байт из /usr/bin/su по целевому смещению файла с помощью splice.splice перемещает данные из канала в отправляющий сокет. splice_to_socket() устанавливает MSG_SPLICE_PAGES. Это помещает страницу страничного кэша /usr/bin/su непосредственно в skb->frags[0].xfrm4_udp_encap_rcv, затем xfrm_input, затем esp_input(). Уязвимая ветвь skip_cow () обходит . Она выполняет , используя страницу страничного кэша и как источник, и как назначение.Атакующий контролирует и местоположение (смещение splice), и значение (4 байта). Проверка подлинности выполняется после операции записи, поэтому криптографический уровень никогда не помечает эту запись. Этот вариант требует CAP_NET_ADMIN и использует unshare(CLONE_NEWUSER|CLONE_NEWNET).
rxkad_verify_packet_1() выполняет расшифровку одного блока pcbc(fcrypt) непосредственно на frag, закреплённом через splice в skb. Он не копирует данные предварительно. Атакующий выбирает сеансовый ключ (add_key("rxrpc", …)) так, чтобы decrypt(ciphertext) равнялся desired_plaintext. Это создаёт STORE размером 8 байт. Этот вариант нацелен на /etc/passwd. Ему не требуется пользовательское пространство имён. Он требует модуль rxrpc.ko (загружается по умолчанию в Ubuntu).
Публичный PoC нацелен на /usr/bin/su. Он записывает 48 ESP-операций STORE по 4 байта каждая (192 байта по смещению файла 0). Он заменяет первые байты страничного кэша статическим ELF-файлом root-оболочки. Точка входа ELF выполняет setgid(0); setuid(0); setgroups(0,NULL); execve("/bin/sh", …). Один вызов execve("/usr/bin/su") затем даёт root-оболочку.
Патч ESP (mainline f4c50a4034e6) помечает страничные frag, поступающие через splice(), флагом SKBFL_SHARED_FRAG. Ветвь skip_cow в esp_input() теперь также проверяет этот флаг. Skb с общими frag проходят через skb_cow_data() перед расшифровкой AEAD на месте.
Патч RxRPC (mainline aa54b1d27fe0) добавляет проверку skb->data_len рядом с существующей проверкой skb_cloned(). Ядро копирует нелинейный skb с разбитыми на страницы данными перед расшифровкой pcbc(fcrypt) на месте.
Скриншот лабораторной настройки VirtualBox:

.
├── README.md # этот журнал лабораторной работы
├── detection/
│ ├── dirtyfrag.rules # правила обнаружения auditd на уровне системных вызовов
│ ├── ausearch_dirtyfrag_observed.txt # реальный вывод обнаружения эксплойта
│ ├── sigma/
│ │ └── dirty_frag_exploit.yml # правило Sigma для обнаружения в SIEM
│ └── yara/
│ └── dirty_frag_exploit.yar # правило YARA для кода PoC на диске/в памяти
├── mitigation/
│ └── dirtyfrag_mitigation.sh # блокировка модулей + очистка страничного кэша
├── poc/
│ └── check_vulnerable.py # неразрушающая предварительная проверка
├── reports/
│ └── incident-dirtyfrag.md # сценарий реагирования на инциденты
└── screenshots/ # реальные снимки из лабораторной ВМ
poc/check_vulnerable.py и подтвердить ядро/модули/usernstestuser/usr/bin/sudrop_caches или перезагрузка)detection/dirtyfrag.rules и проверить оповещения auditdcat /etc/os-release | head -3
uname -r
Скриншот проверки версии ядра:

Ядро должно быть старше исправлений мая 2026 года (f4c50a4034e6 / aa54b1d27fe0). Если вы запускали apt upgrade после этой даты, эксплойт не сработает. См. Устранение неполадок.
Безопасный проверщик сообщает, является ли ВМ вероятной целью. Он проверяет работающее ядро, наличие модулей esp4/esp6/rxrpc и доступность непривилегированных пользовательских пространств имён. Вариант ESP требует этих пространств имён.
python3 poc/check_vulnerable.py
Ожидаемый вердикт: [*] potentially vulnerable -- proceed in a disposable VM only.
Непривилегированный testuser имитирует атакующего без особых прав.
sudo useradd -m testuser
sudo passwd testuser
su - testuser
id
Скриншот: id показывает UID 1001. Это подтверждает доступ без root.

Из непривилегированной учётной записи:
git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag
gcc -O0 -Wall -o exp exp.c -lutil
./exp
При успехе эксплойт модифицирует страничный кэш /usr/bin/su. Он записывает 48 ESP-операций STORE по 4 байта каждая (ELF-файл root-оболочки размером 192 байта по смещению файла 0). Затем он запускает интерактивную root-оболочку с помощью forkpty.
Скриншот доступности модулей, проверки исходного кода и чистой компиляции:

id
whoami
Скриншот: id показывает uid=0(root) после ./exp.

sha256sum читает через страничный кэш. Пока запись эксплойта активна, хэш отличается от исходного. После очистки хэш возвращается к исходному. Эта пара «до/после» доказывает, что двоичный файл на диске никогда не изменялся.
sha256sum /usr/bin/su # 1) пока страничный кэш заражён -> ДРУГОЙ хэш
Скриншот: хэш отличается от хэша пакета (ОЗУ отравлено, диск нетронут).

Наблюдаемый повреждённый (страничный кэш) хэш: 3fc29078bd77150b5d6fbb368f632bf02c1a3704816f03fb694ec2e81e77bde4
После эксплуатации страничный кэш содержит повреждённые данные. Всегда очищайте его:
echo 3 | sudo tee /proc/sys/vm/drop_caches
# или перезагрузите ВМ
Затем проверьте восстановление (как testuser):
sha256sum /usr/bin/su # теперь совпадает с ИСХОДНЫМ хэшем пакета
su - # снова запрашивает пароль — автоматического root нет
Скриншот: восстановленный (исходный на диске) хэш.

Наблюдаемый исходный (на диске) хэш: 2b4f8770bd35bba5cdc5cfe292bc1d988e92ec1786bf91cf83e0e86fac056eb6
После перезагрузки dpkg -V util-linux не вернул никакого вывода. Файл /usr/bin/su на диске точно соответствует пакету, поэтому повреждённый хэш 3fc29078… существовал только в страничном кэше.
drop_cachesможет не удалить отравленную страницу. Это происходит, если работающий процесс всё ещё удерживает страницу. В этом случаеsha256sumиdpkg -Vпродолжают показывать повреждённое содержимое. Надёжное решение — перезагрузка. Обратите внимание, чтоdpkg -Vтакже читает через страничный кэш. Пока страница отравлена, он сообщает??5??????(MD5 отличается; размер, режим, владелец и mtime совпадают). После перезагрузки вывод снова становится пустым. Это доказывает, что файл на диске никогда не изменялся.
Очистка не отключает эксплойт. Она только удаляет отравленный страничный кэш. Модули необходимо отключить отдельно (см. Смягчение). Удаление уже загруженных модулей на скомпрометированном хосте требует перезагрузки.
Dirty Frag невидим для мониторинга целостности файлов. Обнаружение сосредоточено на примитивах системных вызовов, которые цепочка обязана использовать.
detection/dirtyfrag.rules)# /etc/audit/rules.d/dirtyfrag.rules
-a always,exit -F arch=b64 -S socket -F a0=38 -F uid!=0 -k dirtyfrag_af_alg
-a always,exit -F arch=b64 -S socket -F a0=33 -F uid!=0 -k dirtyfrag_rxrpc
-a always,exit -F arch=b64 -S splice -F uid!=0 -k dirtyfrag_splice
-a always,exit -F arch=b64 -S unshare -F uid!=0 -k dirtyfrag_namespace
-w /usr/bin/su -p r -k dirtyfrag_suid_read
Примечание:
AF_ALG = 38иAF_RXRPC = 33в Linux (см./usr/include/bits/socket.h). В ранних черновиках использовалосьa0=21. Это значение неверно. AF_RXRPC — это 33, а не 21.
Развёртывание и проверка:
sudo cp detection/dirtyfrag.rules /etc/audit/rules.d/
sudo systemctl restart auditd # или: sudo auditctl -D && sudo auditctl -R /etc/audit/rules.d/dirtyfrag.rules
sudo auditctl -l
Ожидаемые оповещения после повторного запуска эксплойта (сопоставьте по PID):
ausearch -k dirtyfrag_af_alg
ausearch -k dirtyfrag_rxrpc
ausearch -k dirtyfrag_splice
ausearch -k dirtyfrag_namespace
Все пять правил загружены (auditctl -R, res=1 для каждого CONFIG_CHANGE). Правила зафиксировали настройку пространств имён эксплойтом:
time->Wed Aug 5 08:26:37 2026
type=PROCTITLE msg=audit(1785932797.328:592): proctitle="./exp"
type=SYSCALL msg=audit(1785932797.328:592): arch=c000003e syscall=272 success=yes exit=0 a0=50000000 a1=0 a2=0 a3=0 items=0 ppid=5198 pid=5199 auid=1000 uid=1001 gid=1001 euid=1001 suid=1001 fsuid=1001 egid=1001 sgid=1001 fsgid=1001 tty=pts2 ses=2 comm="exp" exe="/home/testuser/dirtyfrag/exp" subj=unconfined key="dirtyfrag_namespace"
Расшифровка: syscall=272 (unshare), a0=50000000 равно CLONE_NEWUSER | CLONE_NEWNET, uid=1001 (непривилегированный testuser), comm="exp". Полный журнал находится в detection/ausearch_dirtyfrag_observed.txt.
Скриншот: вывод ausearch показывает загрузку правил и событие эксплойта.

Полный сценарий находится в reports/incident-dirtyfrag.md: резюме для руководства, хронология, индикаторы компрометации, сопоставление с MITRE ATT&CK, сдерживание, устранение, восстановление и извлечённые уроки.
Немедленное смягчение во время выполнения. Оно не переживает перезагрузку для уже загруженных модулей (см. примечание ниже):
sudo mitigation/dirtyfrag_mitigation.sh
Что он делает:
/etc/modprobe.d/dirtyfrag.conf для блокировки esp4, esp6 и rxrpc. Он включает строки blacklist и alias … off. Простой однострочник пропускает эти строки (автозагрузка по алиасу в противном случае продолжала работать).Влияние: отключение этих модулей приводит к прекращению работы IPsec VPN (ESP) и файловой системы AFS (RxRPC).
Постоянное исправление: обновите ядро, содержащее патчи вышестоящего проекта. Или заблокируйте модули при загрузке (initcall_blacklist=esp4,esp6,rxrpc).
Этот репозиторий предназначен только для авторизованных оборонительных исследований в области безопасности и обучения.
См. LICENSE. Эта лабораторная работа предназначена для образовательного использования только в вашей собственной изолированной среде. Не запускайте PoC на системах, которые вам не разрешено тестировать.
| Вариант | CVE | Слабое место | Путь срабатывания | Требует непривилегированного userns |
|---|
| Запись в страничный кэш через xfrm‑ESP | CVE‑2026‑43284 | crypto_authenc_esn_decrypt() в esp_input() | socket(AF_INET) с UDP‑encap, затем xfrm_input() | Да (CAP_NET_ADMIN) |
| Запись в страничный кэш через RxRPC | CVE‑2026‑43500 | rxkad_verify_packet_1() (pcbc(fcrypt)) | socket(AF_RXRPC) | Нет |
!skb_cloned() && !skb_has_frag_list()skb_cow_data()crypto_authenc_esn_decrypt() выполняет STORE старших 32 бит ESN. Это значение — replay_esn->seq_hi. Атакующий выбирает это значение при регистрации SA с помощью атрибута netlink XFRMA_REPLAY_ESN_VAL.| Компонент | Детали |
|---|
| Гипервизор | VirtualBox |
| Целевая ВМ | Kali Linux 2026.1 (снимок восстановлен до уязвимого состояния) |
| Ядро | 6.18.9+kali‑amd64 (старше исправлений мая 2026 года) |
| PoC эксплойта | V4bel/dirtyfrag (один C-файл) |
| Обнаружение | auditd (правила в detection/dirtyfrag.rules) |
reports/incident-dirtyfrag.md)| Уровень | Что видит | Статус |
|---|
| auditd | Сокеты AF_ALG и AF_RXRPC, splice, unshare, чтения SUID | Да. Развёрнуто и проверено (файл правил detection/dirtyfrag.rules) |
| Sigma | Паттерны системных вызовов для SIEM | Да. Правило готово (detection/sigma/dirty_frag_exploit.yml) |
| YARA | Код PoC на диске или в памяти | Да. Правило готово (detection/yara/dirty_frag_exploit.yar) |
| FIM (AIDE/Tripwire) | Изменения файлов | Нет. Слеп, поскольку записи на диск не происходит |
| Симптом | Вероятная причина | Исправление |
|---|
./exp выводит failed / post-write verify failed | Ядро пропатчено (мая 2026 года или новее) | Загрузите более старое ядро или восстановите снимок до обновления. Повторно проверьте с помощью poc/check_vulnerable.py |
unshare(CLONE_NEWUSER) возвращает EPERM | Непривилегированные пользовательские пространства имён отключены (AppArmor или sysctl) | Проверьте sysctl kernel.unprivileged_userns_clone. В Ubuntu проверьте AppArmor. Kali разрешает это по умолчанию |
esp4/esp6/rxrpc не загружены | Модули недоступны | sudo modprobe esp4 esp6 rxrpc (RxRPC автоматически загружается с socket(AF_RXRPC)) |
Бинарник su «изменён», но root-оболочки нет | drop_caches уже запущен, или страничный кэш удерживается процессами | Повторно запустите эксплойт. Если застряло, перезагрузите ВМ |
| Эксплойт запущен, но root-доступ всё ещё возможен после смягчения | Страничный кэш не очищен, или модули были загружены до блокировки | echo 3 > /proc/sys/vm/drop_caches. Полная остановка требует перезагрузки |