
Уязвимость, связанная с Linux
Что представляет собой этот репозиторий: моё практическое воспроизведение и исследование CVE-2023-32233 — уязвимости использования после освобождения (use-after-free) в подсистеме nf_tables фреймворка Netfilter ядра Linux, позволяющей локальное повышение привилегий; публично раскрыта в мае 2023 года Патриком Сондеем и Петром Крысюком.
Авторство: PoC (exploit.c) и оригинальный разбор принадлежат @Liuk3r. Моя работа в этом репозитории — воспроизведение эксплойта в моей собственной лаборатории (Ubuntu 23.04, ядро 6.2.0-20) и документирование первопричины и цепочки эксплуатации ниже. Всё тестирование проводилось на выделенных лабораторных машинах.
nf_tables обрабатывает обновления конфигурации как атомарный пакет (batch). Проверка каждой операции на соответствие изменениям состояния предыдущих операций в том же пакете недостаточна. Конкретно:
nft_rule, содержащего выражение lookup для анонимного nft_set, в котором хранятся некоторые элементы.NFT_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:
Если set->ops->elemsize повреждён, выбранное атакующим место в памяти интерпретируется как nft_set_ext — примитив, на котором строится всё остальное.
nf_tables_trans_destroy_work(), выполняющейся в фоновом рабочем потоке: вставить операцию уничтожения большого множества как контролируемую задержку, занять остальные CPU работой и перераспределить освобождённый фрагмент nft_set на том же CPU с помощью nft_set другого типа (другой elemsize) → подмена типа (type confusion).nft_set_ext со смещениями за пределами допустимого диапазона, чтобы nf_tables_set_elem_destroy() обходила соседние фрагменты как список подлежащих уничтожению nft_expr.nft_log с контролируемым NFTA_LOG_PREFIX; nft_log_destroy() освобождает priv->prefix, что даёт примитив перекрывающегося выделения памяти в kmalloc-{8..192} (поначалу ограниченный NULL-байтом).Примечательно, что выбранные примитивы избегают всего, что заблокировала бы 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() зависит от компилятора и опций; полный справочник параметров (nft_counter_destroy_call_offset/mask/check) и рычаги настройки гонки (race_lead_sleep и т.д.) см. в ORIGIN.md. Заявленная вероятность успеха составляет ≥80% на незагруженных bare-metal системах Intel; некоторые микроархитектуры (например, Alder Lake) требуют дополнительной настройки.
После успешного запуска PoC оставляет ядро в нестабильном состоянии с повреждённой памятью. Тестируйте только на выделенной одноразовой системе или снапшоте ВМ — никогда на чём-либо, где есть важные для вас данные.
exploit.c — исходный код PoC (автор: @Liuk3r)ORIGIN.md — оригинальный разбор уязвимости и эксплуатации (автор: Liuk3r)README.md — этот файл: воспроизведение и мои заметки по анализуИсследование выполнено в защитных и образовательных целях в изолированной лабораторной среде.
static inline struct nft_set_ext *nft_set_elem_ext(const struct nft_set *set,
void *elem)
{
return elem + set->ops->elemsize;
}
nft_object->udatanft_dynset для двух типов выражений с состоянием:
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 с содержимым, контролируемым атакующим.