InquirerPower crunch forces Cebu firms to alter work hours, tap generatorsCNN TürkALTIN FİYATLARI 11 EYLÜL 2026 CANLI: Bugün Gram, Çeyrek, Cumhuriyet Ne Kadar? Kapalıçarşı Altın Fiyatları Ne Durumda?PunchAfter PUNCH report, Adamawa CP warns officers against extorting beer sellersESPN DeportesLas predicciones del Gran Premio de España de Fórmula 1Daily MaverickTHE GATHERING 2026: ‘Not too late to turn around SA’s municipalities with the right people in charge’The Jerusalem PostRosh Hashanah: The time to look inward and reveal what is unseenוואלהבכיר בממשלת תימן: החות'ים השתלטו על האי האסטרטגי מיוןInquirer EntertainmentPH-Australian film ‘First Light’ chosen as Australia’s official entry to 2027 OscarsVanguardObasanjo leads dignitaries to London launch of Charly Boy’s memoir ‘999’7sur7Attaque dans un lycée suédois: les autorités suspectent une fille de 15 ans d’avoir préparé un assassinatNOSKabinet steunt Europese leeftijdsgrens sociale mediaגיקטייםמודל ישראלי חדש יודע לדבר עברית טוב יותר מהמודלים של ענקיות הטכנולוגיה
The Daily Newsstand · Free, Always
Friday, September 11, 2026

Как превратить публичный PoC в детект: разбираем эксплуатацию Dirty Frag

Translate

На связи Алина Байрамова, аналитик-исследователь угроз кибербезопасности R-Vision. 

Появление новой уязвимости с рабочим публичным PoC обычно запускает привычный процесс: специалисты определяют затронутые системы, проверяют наличие обновлений и планируют установку патчей. Но до тех пор, пока инфраструктура не обновлена, необходимо понять, можно ли заметить попытку эксплуатации по доступной телеметрии.

С этим не всегда все очевидно. Описание CVE и публичный PoC позволяют понять, как работает уязвимость, но сами по себе не дают готового сценария обнаружения. Эксплойт может использовать штатные механизмы операционной системы, а отдельные действия, которые он выполняет, встречаются и в легитимной активности.

В этой статье разберем три публичных PoC для Dirty Frag: два варианта эксплуатации CVE-2026-43284  через ESP/XFRM и один для CVE-2026-43500 через RxRPC.

Посмотрим, какие системные вызовы выполняют эксплойты, какие следы остаются в событиях Linux и какие признаки можно использовать для обнаружения эксплуатации. В результате соберем детекты для разных вариантов Dirty Frag и сравним, какие признаки остаются стабильными между реализациями PoC.

Что такое Dirty Frag?

Dirty Frag - общее название для двух независимых уязвимостей локального повышения привилегий в Linux: CVE-2026-43284 в подсистеме xfrm ESP и CVE-2026-43500 в модуле RxRPC. Обе уязвимости позволяют атакующему изменить данные файла, доступного только для чтения, в кэше страниц page cache.

Общий принцип эксплуатации выглядит следующим образом: системный вызов splice() передаёт страницы файла по цепочке файл → pipe → сетевой сокет без копирования в пользовательское пространство. В результате фрагмент сетевого буфера skb может ссылаться непосредственно на страницу исходного файла.

Перед изменением такого фрагмента ядро должно создать отдельную копию данных. Однако в уязвимых путях ESP и RxRPC криптографическая обработка выполняется непосредственно над разделяемой страницей. Поэтому изменение сетевого пакета одновременно изменяет кэшированную копию /etc/passwd или usr/bin/su, или другого читаемого файла.

В CVE-2026-43284 этот путь проходит через обработчик ESP, а в CVE-2026-43500 - через проверку пакета RxRPC.

Подробно останавливаться на внутреннем устройстве уязвимости не будем. Подробное описание внутренних структур ядра и обеих веток эксплуатации приведено в разборе Ideco и оригинальном исследовании Hyunwoo Kim.

Для этой статьи важнее другое: какие действия выполняют конкретные PoC и какие следы они оставляют в телеметрии Linux.

Начнем с первого ESP/XFRM-варианта.

PoC № 1: Copy_Fail2-Electric_Boogaloo - CVE-2026-43284

Первый PoC опубликован в репозитории Copy_Fail2-Electric_Boogaloo. Это компактная реализация эксплуатации CVE-2026-43284 через ESP/XFRM.

Скрипт выбирает в /etc/passwd длинную запись пользователя с оболочкой nologin, false или sync, сохраняет исходную строку в /var/tmp/.cf2.state и формирует запись вида sick::0:0:<padding>:/bin/bash. Затем для каждого отличающегося байта запускается copyfail2 с целевым смещением и нужным значением.

Сам бинарный файл открывает целевой файл только на чтение. Страница попадает в pipe и UDP-сокет через splice(). Перед этим на сокете включается UDP_ENCAP, чтобы переданные данные прошли через ESP-обработчик ядра. После изменения page cache скрипт перечитывает /etc/passwd и выполняет su - sick.

Ключевая последовательность:

socket(AF_INET, SOCK_DGRAM, 0)
     → setsockopt(IPPROTO_UDP, UDP_ENCAP)
     → bind(127.0.0.1:4500)
     → splice(файл → pipe)
     → splice(pipe → UDP-сокет)
     → чтение /etc/passwd
     → su - sick

Таким образом, эксплуатация строится на последовательности:

В тестовой среде PoC запускался непривилегированным пользователем командой: ./run.sh

Результат выполнения PoC

Результат выполнения PoC

При проверке, видим что текущий пользователь получил привилегии root.

Анализ событий и способы обнаружения эксплуатации

При запуске ESP-варианта PoC в SIEM фиксируется повторяющаяся последовательность системных вызовов:

socket(AF_INET, SOCK_DGRAM, 0)
 → setsockopt(IPPROTO_UDP, UDP_ENCAP)
 → splice

Последовательность повторяется при обработке отдельных фрагментов полезной нагрузки; ниже приведён один из таких фрагментов. Оранжевым выделены название системного вызова, его аргументы, идентификатор процесса и имя пользователя.

События системных вызовов, соответствующие цепочке ESP-варианта Copy_Fail2

 События системных вызовов, соответствующие цепочке ESP-варианта Copy_Fail2

В поле dst_object_name указано название системного вызова, а в dst_object_value — переданные ему аргументы.

Для socket значения a0=2, a1=2 и a2=0 соответствуют AF_INET, SOCK_DGRAM и IPPROTO_IP.

Вызов setsockopt включает UDP-инкапсуляцию. Значения a1=11 и a2=64 соответствуют IPPROTO_UDP и UDP_ENCAP.

Для splice достаточно учитывать успешный вызов с действием copy. Аргументы a0–a3 в правило не включаются, поскольку содержат файловые дескрипторы и указатели на смещения, которые могут различаться между операциями.

По отдельности эти системные вызовы не являются признаком эксплуатации. Для обнаружения важна именно их последовательность в рамках одного процесса.

На основе этих событий можно собрать следующее правило корреляции:

filter: !vrl |
  .device_vendor == "linux" &&
  .device_product == "auditd" &&
  .outcome == "success" &&
  .src_user_id != "4294967295" &&
  includes(["socket", "setsockopt", "splice"], .dst_object_name)

aliases:
  udp_socket:
    filter: !vrl |
      object_value, _err = downcase(.dst_object_value)
      .dst_object_name == "socket" &&
      contains_all(object_value, ["a0=2", "a1=2", "a2=0"])

  udp_encap:
    filter: !vrl |
      object_value, _err = downcase(.dst_object_value)
      .dst_object_name == "setsockopt" &&
      contains_all(object_value, ["a1=11", "a2=64"])

  splice_call:
    filter: !vrl |
      .dst_object_name == "splice" &&
      .action == "copy"

select:
  alias: udp_socket
  join:
    alias: udp_encap
    on:
      - eq: { udp_socket: .device_hostname, udp_encap: .device_hostname }
      - eq: { udp_socket: .src_process_id, udp_encap: .src_process_id }
      - eq: { udp_socket: .src_user_name, udp_encap: .src_user_name }

    join:
      alias: splice_call
      on:
        - eq: { udp_encap: .device_hostname, splice_call: .device_hostname }
        - eq: { udp_encap: .src_process_id, splice_call: .src_process_id }
        - eq: { udp_encap: .src_user_name, splice_call: .src_user_name }

PoC № 2: dirtyfrag - ESP-ветка CVE-2026-43284

Второй репозиторий - V4bel/dirtyfrag. Его exp.c содержит универсальную обвязку и два пути эксплуатации. Сначала разберём ветку ESP.

Здесь целевой объект - /usr/bin/su. PoC собирает небольшой ELF-файл, который после запуска выполняет команды повышения привилегий и запускает /bin/sh. Полезная нагрузка разделяется на фрагменты по четыре байта, а для каждого фрагмента создаётся отдельная XFRM Security Association.

В отличие от Copy_Fail2, заголовок ESP помещается в pipe через vmsplice(). Затем splice() добавляет фрагмент /usr/bin/su и передаёт буфер в UDP-сокет. XFRM SA регистрируется через netlink, а необходимые capability подготавливаются в user и network namespace.

Ключевая последовательность:

Для запуска ESP/XFRM-варианта PoC используется параметр --force-esp:

./exp --force-esp -v
Успешная эксплуатация CVE-2026-43284: изменение кэшированной копии /usr/bin/su через XFRM/ESP и получение root-оболочки

Успешная эксплуатация CVE-2026-43284: изменение кэшированной копии /usr/bin/su через XFRM/ESP и получение root-оболочки

В результате запуска ESP-варианта PoC были изменены первые 192 байта кэшированной копии /usr/bin/su, начиная со смещения 0x0.

Вместо исходных данных PoC помещает туда небольшой ELF-образ, который при запуске устанавливает UID и GID процесса в 0 и запускает root-оболочку.

Анализ событий и способы обнаружения эксплуатации

В ходе запуска ESP-варианта PoC процесс регистрирует параметры XFRM, создаёт UDP-сокеты, включает UDP-инкапсуляцию и передаёт подготовленные данные через splice().

Для обнаружения эксплуатации выделим основную последовательность системных вызовов:

socket(AF_INET, SOCK_DGRAM, 0)
→ setsockopt(IPPROTO_UDP, UDP_ENCAP)
→ splice()

PoC выполняет эту последовательность многократно. Ниже показаны нормализованные события из R-Vision SIEM. Оранжевым выделены название системного вызова, его аргументы, идентификатор процесса и имя пользователя.

Фрагмент цепочки системных вызовов ESP-варианта PoC dirtyfrag

 Фрагмент цепочки системных вызовов ESP-варианта PoC dirtyfrag

Параметры вызовов socket и setsockopt, а также логика обработки splice совпадают с первым сценарием.

Это важный результат сравнения двух PoC. Несмотря на различия в подготовке полезной нагрузки и внутренней логике, обе реализации CVE-2026-43284 оставляют одинаковый ключевой след на уровне auditd.

Поэтому отдельное правило для второго PoC не требуется. Одна последовательность системных вызовов покрывает оба рассмотренных ESP/XFRM-варианта:

socket(AF_INET, SOCK_DGRAM, 0)
→ setsockopt(IPPROTO_UDP, UDP_ENCAP)
→ splice()

Для второй уязвимости Dirty Frag эта логика уже не подходит: механизм эксплуатации и вызываемая последовательность системных вызовов отличаются.

PoC № 3: dirtyfrag - CVE-2026-43500, RxRPC

Здесь PoC не использует UDP_ENCAP, поэтому последовательность

socket(AF_INET, SOCK_DGRAM, 0)
→ setsockopt(IPPROTO_UDP, UDP_ENCAP)
→ splice()

для этого варианта не формируется.

Сначала PoC добавляет в keyring процесса специально сформированный RxRPC-ключ с помощью add_key(). Затем создается сокет семейства AF_RXRPC:

socket(AF_RXRPC, SOCK_DGRAM, PF_INET)

После этого через setsockopt() задаются параметры RxRPC:

setsockopt(fd, SOL_RXRPC, RXRPC_SECURITY_KEY, ...)
→ setsockopt(fd, SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL, ...)

Далее PoC запускает локальный RxRPC-сеанс и формирует служебные пакеты для прохождения проверки rxkad.

Заголовок вредоносного пакета помещается в pipe через vmsplice(), а содержимое целевого файла передается туда с помощью splice().

Таким образом, в этой цепочке могут встречаться два разных типа сокетов: обычный UDP-сокет, через который передаётся сформированный пакет, и сокет AF_RXRPC, на котором пакет обрабатывается RxRPC-подсистемой. Именно наличие AF_RXRPC помогает отличить данный сценарий от ESP/XFRM-варианта.

При обработке пакета функция rxkad_verify_packet_1() расшифровывает первые 8 байт полезной нагрузки непосредственно в исходном буфере. Если этот буфер связан со страницей page cache, результат расшифрования записывается в ту же страницу памяти.

PoC заранее подбирает ключ и содержимое токена, чтобы результат расшифрования дал требуемые байты. Затем изменённые данные могут использоваться для подмены записи в /etc/passwd и получения root-доступа. Подробная последовательность вызовов приведена в исходном коде PoC.

Для отдельного тестирования RxRPC-ветки PoC запускается командой:

./exp --force-rxrpc

В результате выполнения PoC запись пользователя root в кэшированной копии файла /etc/passwd изменяется: поле пароля становится пустым. После этого атакующий получает root-оболочку.

Успешная эксплуатация CVE-2026-43500: изменение записи root в page cache файла /etc/passwd и подтверждение результата с помощью getent passwd

Успешная эксплуатация CVE-2026-43500: изменение записи root в page cache файла /etc/passwd и подтверждение результата с помощью getent passwd

Анализ событий и способы обнаружения эксплуатации

В RxRPC-варианте отсутствует вызов setsockopt(IPPROTO_UDP, UDP_ENCAP). Основная последовательность для обнаружения эксплуатации выглядит так:

setsockopt(SOL_RXRPC, RXRPC_SECURITY_KEY)
→ setsockopt(SOL_RXRPC, RXRPC_MIN_SECURITY_LEVEL)
→ splice()

Первые два вызова настраивают параметры безопасности RxRPC, после чего PoC передаёт подготовленные данные через pipe с помощью splice(). На скриншоте показан фрагмент этой последовательности. Оранжевым выделены идентификатор процесса, имя пользователя, названия системных вызовов и значимые аргументы.

Фрагмент событий системных вызовов RxRPC-варианта PoC

 Фрагмент событий системных вызовов RxRPC-варианта PoC

Все события были выполнены одним процессом от имени одного пользователя, поэтому для корреляции также можно использовать имя хоста, идентификатор процесса и имя пользователя.

В первом вызове setsockopt (syscall=54) значение a1=110 обозначает уровень SOL_RXRPC, а a2=1 — параметр RXRPC_SECURITY_KEY. Значение a0 является файловым дескриптором сокета, а a3 — указателем на структуру параметров.

Во втором вызове setsockopt сохраняется тот же уровень a1=110, но a2=4 соответствует параметру RXRPC_MIN_SECURITY_LEVEL.

Для splice (syscall=275) учитывается успешный вызов с действием copy. Его аргументы не фиксируются: a0 и a2 обозначают файловые дескрипторы, а a1 и a3 — указатели на смещения, поэтому могут различаться между операциями.

Две последовательные настройки setsockopt с уровнем SOL_RXRPC являются более специфичным признаком, чем обычный вызов setsockopt, поскольку указывают на подготовку RxRPC-сеанса.

В ходе одного запуска PoC может появляться несколько вызовов splice() с разными файловыми дескрипторами. Это связано с тем, что программа отдельно помещает заголовок в pipe, переносит в pipe данные целевого файла, а затем передаёт объединённый буфер в UDP-сокет.

По описанным событиям можно составить правило корреляции, в котором события связываются по имени хоста, идентификатору процесса и имени пользователя. Это позволяет отличить последовательность действий PoC от отдельных легитимных вызовов setsockopt или splice.

Ниже приведён пример корреляционного фильтра на языке VRL:

filter: !vrl |
  .device_vendor == "linux" &&
  .device_product == "auditd" &&
  .device_event_id == "1300" &&
  .device_action == "syscall" &&
  .outcome == "success" &&
  includes(["setsockopt", "splice"], .dst_object_name)

aliases:
  rxrpc_security_key:
    filter: !vrl |
      dst_object_value, _err = downcase(.dst_object_value)
      .dst_object_name == "setsockopt" &&
      contains_all(dst_object_value, ["a1=110,", "a2=1,"])

  rxrpc_min_security:
    filter: !vrl |
      dst_object_value, _err = downcase(.dst_object_value)
      .dst_object_name == "setsockopt" &&
      contains_all(dst_object_value, ["a1=110,", "a2=4,"])

  splice_call:
    filter: !vrl |
      .dst_object_name == "splice" &&
      .action == "copy"

select:
  alias: rxrpc_security_key
  join:
    alias: rxrpc_min_security
    on:
      - eq: { rxrpc_security_key: .device_hostname, rxrpc_min_security: .device_hostname }
      - eq: { rxrpc_security_key: .src_process_id, rxrpc_min_security: .src_process_id }
      - eq: { rxrpc_security_key: .src_user_name, rxrpc_min_security: .src_user_name }

    join:
      alias: splice_call
      on:
        - eq: { rxrpc_min_security: .device_hostname, splice_call: .device_hostname }
        - eq: { rxrpc_min_security: .src_process_id, splice_call: .src_process_id }
        - eq: { rxrpc_min_security: .src_user_name, splice_call: .src_user_name }

Настройка auditd

Чтобы отслеживать системные вызовы, используемые PoC, и передавать их в коррелятор SIEM, необходимо предварительно настроить auditd.

Приведённые ниже правила рассчитаны на 64-битную архитектуру x86_64. В сырых audit-событиях числовые аргументы обычно отображаются в шестнадцатеричном виде без префикса 0x. Например, a1=110 соответствует 0x110, то есть 272 в десятичной системе.

# x86_64, IPv4 UDP socket
-a always,exit -F arch=b64 -S socket -F a0=0x2 -F a1=0x2 -F a2=0x0 -F success=1 -F key=dirtyfrag_socket_v4

# x86_64, IPv6 UDP socket
-a always,exit -F arch=b64 -S socket -F a0=0xa -F a1=0x2 -F a2=0x0 -F success=1 -F key=dirtyfrag_socket_v6

# IPPROTO_UDP = 0x11, UDP_ENCAP = 0x64
-a always,exit -F arch=b64 -S setsockopt -F a1=0x11 -F a2=0x64 -F success=1 -F key=dirtyfrag_encap

-a always,exit -F arch=b64 -S splice -F auid>=1000 -F auid!=-1 -F success=1 -F key=dirtyfrag_splice

# SOL_RXRPC = 0x110 = 272
-a always,exit -F arch=b64 -S setsockopt -F a1=272 -F a2=1 -F success=1 -F key=dirtyfrag_rxrpc_security_key

Эти правила позволяют отдельно фиксировать ключевые вызовы, которые используются в рассмотренных PoC: создание UDP-сокета, настройку UDP_ENCAP, вызов splice, а также параметры setsockopt, характерные для RxRPC-варианта эксплуатации.

Какие системы могут быть уязвимы?

Уязвимость в xfrm-ESP присутствует в ядре Linux с января 2017 года, в RxRPC — с июня 2023 года. Эксплоит, подготовленный исследователем, работает в нескольких крупных дистрибутивах, комбинируя обе уязвимости.

Подтверждена работа:

  • Ubuntu 24.04.4 с ядром 6.17.0-23

  • RHEL 10.1 с ядром 6.12.0-124.49.1

  • openSUSE Tumbleweed с ядром 7.0.2-1

  • CentOS Stream 10 с ядром 6.12.0-224

  • AlmaLinux 10 с ядром 6.12.0-124.52.3

  • Fedora 44 с ядром 6.19.14-300

Кроме того, успешность эксплуатации зависит от конфигурации системы:

  • разрешено создание user namespace;

  • модули esp4, esp6 и rxrpc загружены либо доступны для загрузки;

  • разрешена загрузка этих модулей;

  • AppArmor или SELinux не блокируют необходимые операции;

  • используется уязвимая версия ядра.

Меры защиты

Основная мера защиты — обновить ядро до пакета дистрибутива, содержащего исправления для обеих веток Dirty Frag:

  • исправление ESP/XFRM — upstream-коммит f4c50a4;

  • исправление RxRPC — upstream-коммит aa54b1d.

Если обновление временно невозможно, можно рассмотреть блокировку загрузки уязвимых модулей. Делать это следует только после проверки роли узла и зависимости сервисов от IPsec, ESP, AFS и RxRPC.

sudo sh -c 'printf "install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n" > /etc/modprobe.d/dirtyfrag.conf'

sudo modprobe -r esp4 esp6 rxrpc

Если эксплуатация уже произошла, сначала необходимо изолировать узел и сохранить доступные артефакты. После этого можно сбросить page cache:

echo 3 > /proc/sys/vm/drop_caches

Вывод

Сетевые и криптографические механизмы Linux обычно воспринимаются как фоновая системная активность. Однако Dirty Frag показывает, что они могут стать частью цепочки локального повышения привилегий. Используя штатные механизмы splice(), сетевые сокеты и обработку пакетов, атакующий способен изменить страницу читаемого файла в page cache без обычного write(). Для CVE-2026-43284 этот путь проходит через xfrm/ESP, а для CVE-2026-43500 — через RxRPC.

Разбор публичных PoC позволяет выделить практические признаки такой активности. Для ESP/XFRM это последовательность:

socket
→ setsockopt(UDP_ENCAP)
→ splice()

Для RxRPC:

AF_RXRPC
→ add_key
→ setsockopt
→ splice
→ recvmsg

Эти события следует коррелировать по одному хосту и процессу в коротком временном окне.

Предложенные правила позволяют обнаружить попытку пройти уязвимым путём, но не подтверждают успешное повышение привилегий. Для этого нужны дополнительные признаки: запуск процесса с UID 0, выполнение su или другого setuid-бинарника, проверка целевого файла после очистки page cache или перезагрузки, а также анализ версии и конфигурации ядра.

Основной мерой защиты остаётся установка исправленного ядра. Если обновление временно невозможно, можно ограничить загрузку модулей esp4, esp6 и rxrpc, предварительно оценив влияние на IPsec, AFS и другие сервисы.

Источники

  1. CVE-2026-43284 в NVD.

  2. Официальная запись CVE.

  3. PoC Copy_Fail2-Electric_Boogaloo.

  4. PoC dirtyfrag и его технический разбор.

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.