Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

ЛентыКонтактыКонфиденциальность© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
POC-CVE-2023-32233 — Использование после освобождения в Netfilter nf_tables при обработке пакетных запросов CVE-2023-32233 | Kitploit
Инструменты/GitHubGitHub/oferchen/poc-cve-2023-32233
Повышение привилегийКриминалистика памятиАнализ уязвимостейЭксплуатацияТестирование на ПроникновениеРазработка Полезной НагрузкиЭксплуатация Бинарных Файлов
GitHuboferchen/poc-cve-2023-32233

POC-CVE-2023-32233

Использование после освобождения в Netfilter nf_tables при обработке пакетных запросов CVE-2023-32233

Репозиторий
536183 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Использование после освобождения (Use-After-Free) в Netfilter nf_tables при обработке пакетных запросов

Демонстрация

Demo_CVE-2023-32233

Сведения об уязвимости

Затронутый код происходит из официального ядра Linux с https://kernel.org/ и является частью компонента Netfilter nf_tables (net/netfilter/nf_tables_api.c).

Netfilter nf_tables позволяет обновлять свою конфигурацию как атомарную операцию. При использовании этой функции клиенты пользовательского режима отправляют пакетные запросы, содержащие список базовых операций. Netfilter nf_tables затем обрабатывает все операции в рамках пакета как единую транзакцию. При обработке пакета Netfilter nf_tables проверяет обновления состояния конфигурации, чтобы убедиться, что каждая последующая базовая операция допустима, и это также учитывает обновления состояния от всех предыдущих операций в рамках пакета. Однако текущая реализация проверки является недостаточной.

В нашем конкретном сценарии мы начинаем с конфигурации Netfilter nf_tables, которая содержит nft_rule с выражением lookup на анонимном nft_set, и где анонимный nft_set содержит некоторые элементы. Затем мы отправляем пакетный запрос, содержащий следующие две базовые операции:

  1. Операция NFT_MSG_DELRULE для удаления nft_rule.
    Обратите внимание, что это также неявно удаляет выражение lookup и анонимный nft_set.
  2. Операция NFT_MSG_DELSETELEM для удаления любого из элементов удалённого анонимного nft_set.

Текущая версия Netfilter nf_tables принимает указанный выше пакетный запрос. Затем она вызывает nf_tables_commit_release(), которая добавляет освобождённые ресурсы в nf_tables_destroy_list. nf_tables_destroy_list затем обрабатывается nf_tables_trans_destroy_work(), которая сначала освобождает ресурсы, связанные с операцией NFT_MSG_DELRULE, вызывая:

nft_commit_release()
    nf_tables_rule_destroy()
        nf_tables_expr_destroy()
            expr->ops->destroy() что указывает на nft_lookup_destroy()
                nf_tables_destroy_set()
                    nft_set_destroy()
                        kvfree() которая освобождает память, используемую `nft_set`

перед обработкой операции NFT_MSG_DELSETELEM, где ссылка на освобождённый nft_set получается через nft_trans_elem_set() во время следующих вызовов:

nft_commit_release()
    nf_tables_set_elem_destroy()
        nft_set_elem_ext()

В nft_set_elem_ext() выше, местоположение памяти освобождённого nft_set используется для определения местоположения nft_set_ext:

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_expr, подлежащих уничтожению:

static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
                                       const struct nft_set *set, void *elem)
{
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);

        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS))
                nft_set_elem_expr_destroy(ctx, nft_set_ext_expr(ext));

Методы эксплуатации

Эксплуатация вышеуказанной уязвимости требует выигрыша состояния гонки с nf_tables_trans_destroy_work(), которая выполняется из фонового рабочего потока ядра Linux. Это, по-видимому, усложняет практическую эксплуатацию даже до рассмотрения существующих средств смягчения, таких как укрепление аллокатора slab ядра, рандомизация адресного пространства ядра (KASLR) и, особенно, целостность потока управления (Control-Flow Integrity). Однако приложенный PoC доказывает, что всё ещё возможно добиться достаточно надёжной эксплуатации на практике.

Чтобы использовать уязвимость, нам нужно изменить содержимое памяти из nft_set после его освобождения в nf_tables_rule_destroy(), но до того, как он будет использован в nf_tables_set_elem_destroy(). Оба вызова nf_tables_rule_destroy() и nf_tables_set_elem_destroy() выполняются в рамках одного вызова nf_tables_trans_destroy_work(), который выполняется из фонового рабочего потока ядра Linux. Кроме того, освобождённый фрагмент памяти обычно доступен для повторного использования только на том же ядре CPU.

При гонке с nf_tables_trans_destroy_work() мы увеличиваем свои шансы, добавляя контролируемую задержку для фонового рабочего потока между вызовами nf_tables_rule_destroy() и nf_tables_set_elem_destroy(). Для этого мы вставляем дополнительную операцию по уничтожению другого nft_set, содержащего большое количество элементов. Кроме того, мы держим все остальные ядра CPU занятыми, так что фоновый рабочий поток, скорее всего, будет запланирован на определённом ядре CPU, и мы можем попытаться выделить новую структуру на том же ядре CPU сразу после того, как он освободит nft_set в nf_tables_rule_destroy(). Наша цель — выделить новый nft_set другого типа, чтобы повторно использовать местоположение памяти nft_set, освобождённого в nf_tables_rule_destroy().

Новый тип nft_set выбирается так, чтобы использовать другое значение для set->ops->elemsize. Поэтому, когда фоновый рабочий поток наконец вызывает nf_tables_set_elem_destroy() для обработки операции NFT_MSG_DELSETELEM, он интерпретирует свой аргумент elem неправильно, так что повреждённый nft_set_ext *ext оказывается на несколько байт после правильного местоположения. Это означает, что определённое пользователем поле данных исходного nft_set_ext теперь интерпретируется как заголовки, что приводит к путанице типов.

Один из способов злоупотребить этой путаницей типов — подделать заголовки повреждённого nft_set_ext со значениями смещений таким образом, чтобы nf_tables_set_elem_destroy() интерпретировала содержимое любых смежных блоков памяти как список nft_expr для уничтожения через следующие вызовы:

nft_set_elem_expr_destroy()
    __nft_set_elem_expr_destroy()
        nf_tables_expr_destroy()
            expr->ops->destroy()

На этом этапе эксплуатации у нас ещё нет деталей макета памяти ядра. Поэтому невозможно сформировать абсолютные адреса указателей. Однако при создании повреждённых заголовков nft_set_ext мы всё ещё можем использовать смещения вне диапазона, так что expr->ops->destroy() вызывается на определённых валидных nft_expr в смежных блоках памяти.

Для этого мы распыляем выражения nft_log с контролируемым NFTA_LOG_PREFIX. Затем nft_log->prefix освобождается функцией nft_log_destroy(), когда expr->ops->destroy() вызывается:

static void nft_log_destroy(const struct nft_ctx *ctx,
                            const struct nft_expr *expr)
{
        struct nft_log *priv = nft_expr_priv(expr);
        struct nf_loginfo *li = &priv->loginfo;

        if (priv->prefix != nft_log_null_prefix)
                kfree(priv->prefix);

Обратите внимание, что мы всё ещё можем получить доступ к этой памяти и даже снова освободить её через другую ссылку из распылённого выражения nft_log.

Кроме того, мы можем контролировать размер nft_log->prefix, так что он может быть выделен из любого из slab-кэшей kmalloc-{8, ..., 192}. Наконец, упомянутая память интерпретируется ядром как строка символов, поэтому нет необходимости беспокоиться о повреждениях при наложении разных объектов поверх неё. По сути, это конец игры.

Скачать инструмент