
Воспроизведение и анализ первопричины CVE-2023-32233 — use-after-free в nf_tables ядра Linux, позволяющей локальное повышение привилегий, с PoC и заметками по эксплуатации.
Что это за репозиторий: моё практическое воспроизведение и изучение CVE-2023-32233 — уязвимости use-after-free в подсистеме Netfilter nf_tables ядра Linux, позволяющей локальное повышение привилегий, публично раскрытой в мае 2023 года Патриком Сондеем (Patryk Sondej) и Петром Крысюком (Piotr Krysiuk).
Благодарности: PoC (exploit.c) и оригинальное описание принадлежат
@Liuk3r. Моя работа в этом
репозитории — воспроизведение эксплойта в собственной лаборатории
(Ubuntu 23.04, ядро 6.2.0-20) и документирование первопричины и цепочки
эксплуатации ниже. Все тесты проводились на выделенных лабораторных машинах.
nf_tables обрабатывает обновления конфигурации как атомарный батч. Проверка каждой операции на соответствие изменениям состояния от предыдущих операций в том же батче недостаточна. Конкретно:
nft_rule, содержащего выражение lookup для анонимного
, который содержит некоторые элементы.nft_setNFT_MSG_DELRULE — удаляет правило, что неявно удаляет выражение
lookup и анонимный nft_set;NFT_MSG_DELSETELEM — удаляет элемент уже удалённого анонимного
множества.nf_tables_commit_release() помещает ресурсы в
nf_tables_destroy_list, обрабатываемый позже
nf_tables_trans_destroy_work():
сначала nft_commit_release() → nf_tables_rule_destroy() →
nft_lookup_destroy() → nft_set_destroy() → kvfree() освобождает
nft_set;
затем, для NFT_MSG_DELSETELEM, nf_tables_set_elem_destroy() вызывает
nft_set_elem_ext(), который разыменовывает освобождённый nft_set:
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
Если set->ops->elemsize повреждён, выбранная атакующим область памяти
интерпретируется как nft_set_ext — примитив, на котором строится всё
остальное.
nf_tables_trans_destroy_work(), выполняющегося
в фоновом рабочем потоке: вставить большую операцию уничтожения множества
в качестве контролируемой задержки, занять другие CPU работой и
перераспределить освобождённый чанк nft_set на том же CPU с nft_set
другого типа (другой elemsize) → путаница типов.nft_set_ext с выходящими за
границы смещениями, чтобы nf_tables_set_elem_destroy() обходил соседние
чанки как список nft_expr для уничтожения.nft_log с контролируемым NFTA_LOG_PREFIX;
nft_log_destroy() освобождает priv->prefix, давая примитив
перекрывающегося выделения в kmalloc-{8..192} (сначала ограниченный
NULL-байтом).nft_object->udata, чтобы снять ограничение
NULL-байта на чтение из висячего указателя.nft_dynset для двух типов stateful-выражений:
nft_counter — утечка nft_counter_ops → база nf_tables.ko
(обходит KASLR для модуля);nft_quota — указатель consumed даёт произвольное чтение
(NFT_MSG_GETSETELEM → nft_quota_do_dump() → NFTA_QUOTA_CONSUMED)
и произвольную запись (nft_overquota() добавляет skb->len к
*priv->consumed при трафике через loopback).modprobe_path ("/sbin/modprobe" → "//tmp/modprobe") →
выполнение процесса с правами root с содержимым, контролируемым атакующим.Примечательно, что выбранные примитивы обходят всё, что мог бы заблокировать CFI — на любом шаге не требуется перехват косвенных вызовов.
SLAB_FREELIST_HARDENED), INIT_ON_FREE и ограничения повторного
использования slab на уровне CPU — все противодействуют шагу 1; CFI (где
присутствует) ограничивает шаг 3+.nf_tables, содержащие DELRULE+DELSETELEM для анонимных множеств; триаж
крашей для nft_set_elem_ext / nf_tables_trans_destroy_work в стеке.nftables требует CAP_NET_ADMIN, но
непривилегированные пользовательские пространства имён предоставляют его —
именно поэтому kernel.unprivileged_userns_clone=0 является значимой
настройкой усиления защиты.Протестировано под Ubuntu 23.04 (Lunar Lobster), ядро 6.2.0-20-generic.
sudo apt install gcc libmnl-dev libnftnl-dev
gcc -Wall -o exploit exploit.c -lmnl -lnftnl
Встроенный профиль нацелен на бинарные ядра Ubuntu 23.04
(linux-image-6.2.0-20-generic 6.2.0-20.20). Для тестирования других ядер
извлеките символы в файл profile:
modprobe nf_tables
egrep ' (nft_counter_ops|nft_counter_destroy|free_percpu|modprobe_path)(\s|$)' /proc/kallsyms > profile
Машинный код nft_counter_destroy() зависит от компилятора и опций; см.
ORIGIN.md для полного справочника параметров
(nft_counter_destroy_call_offset/mask/check) и настроек тонкой подстройки
гонки (race_lead_sleep и т. д.). Заявленная вероятность успеха — ≥80% на
простаивающих bare-metal системах Intel; некоторые микроархитектуры
(например, Alder Lake) требуют дополнительной подстройки.
PoC оставляет ядро в нестабильном состоянии с повреждённой памятью после успешного запуска. Тестируйте только на выделенной одноразовой системе или снимке VM — никогда на чём-либо с важными для вас данными.
exploit.c — исходный код PoC (от @Liuk3r)ORIGIN.md — оригинальное описание уязвимости и эксплуатации (от Liuk3r)README.md — этот файл: воспроизведение + мои заметки по анализуИсследование проводилось в защитных/образовательных целях в изолированной лабораторной среде.