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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2023-2598 — Технический анализ и PoC-эксплойт для CVE-2023-2598 — уязвимости повышения привилегий в ядре Linux в регистрации буферов io_uring, с подробным объяснением Compound Page и внутреннего устройства folio. | Kitploit
Инструменты/GitHubGitHub/cainiao159357/cve-2023-2598
Повышение привилегийАнализ уязвимостейЭксплуатацияСтатьи и ИсследованияОбучение и ОбразованиеЭксплуатация Бинарных Файлов
GitHubcainiao159357/cve-2023-2598

CVE-2023-2598

Технический анализ и PoC-эксплойт для CVE-2023-2598 — уязвимости повышения привилегий в ядре Linux в регистрации буферов io_uring, с подробным объяснением Compound Page и внутреннего устройства folio.

Репозиторий
52 лет назадЕщё не проверено

Популярное

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

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

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

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

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

CVE-2023-2598 Повышение привилегий

Понимание механизмов Compound Page и folio в Linux через CVE-2023-2598 с целью последующего использования 1day CVE-2023-6560

Compound Page (huge page)

Объём памяти постоянно растёт, но базовый размер страницы в Linux по-прежнему составляет 4 КБ, что уже неэффективно. Поэтому были введены составные страницы для решения этой проблемы. Составная страница — это набор из нескольких страниц, объединённых в одну логическую единицу, при этом две или более физически смежных страницы компонуются вместе, и во многих аспектах их можно рассматривать как одну большую страницу. Они чаще всего используются для создания больших страниц в hugetlbfs или в подсистеме прозрачных больших страниц (transparent huge pages), но также встречаются и в других сценариях. Составные страницы могут использоваться как анонимная память или как буферы в ядре; однако они не могут появляться в page cache, так как page cache работает только с отдельными страницами.

Выделение составной страницы осуществляется вызовом alloc_pages() с установленным флагом __GFP_COMP и порядком страниц более 1 (то есть order >= 1). Это обусловлено механизмом реализации составных страниц.

Примечание: составные страницы обязательно физически непрерывны.

В первой странице (head page) флаг устанавливается как PG_head — маркер того, что это заголовочная страница составной страницы.

Все последующие страницы настраиваются с двумя свойствами: mapping и compound_head. С помощью compound_head определяется, является ли страница хвостовой или заголовочной. Подробнее в функции compound_head().

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

static inline unsigned long _compound_head(const struct page *page)
{
        unsigned long head = READ_ONCE(page->compound_head);
 
        if (unlikely(head & 1))
                return head - 1;
        return (unsigned long)page;
}

Видно, что это поле содержит не только флаг, но и указатель на head page.

Поэтому при получении page легко определить, является ли он составной страницей, и если да, то является ли он head page или tail page. Однако не хватает ключевой информации — размера составной страницы. Если размер неизвестен, то при освобождении составной страницы его необходимо знать. Вся эта информация хранится в поле lru первой хвостовой страницы: размер (order) составной страницы сначала приводится к типу указателя, затем сохраняется в lru.prev, а деструктор — в lru.next.

Зная head page и размер составной страницы, можно корректно освободить эту большую страницу, так как составные страницы физически непрерывны.

Структура показана на рисунке:

img

folio

folio можно рассматривать как лёгкую обёртку над page, без накладных расходов. folio может быть как отдельной страницей, так и составной страницей.

img

На рисунке выше — схема структуры page: 64 байта управляют flags, lru, mapping, index, private, {ref_, map_}count, memcg_data и т.д. Когда page является составной страницей, указанные выше flags и другая информация находятся в head page, а tail page повторно использует поля для управления compound_{head, mapcount, order, nr, dtor} и т.д.

struct folio {
        /* private: don't document the anon union */
        union {
                struct {
        /* public: */
                        unsigned long flags;
                        struct list_head lru;
                        struct address_space *mapping;
                        pgoff_t index;
                        void *private;
                        atomic_t _mapcount;
                        atomic_t _refcount;
#ifdef CONFIG_MEMCG
                        unsigned long memcg_data;
#endif
        /* private: the union with struct page is transitional */
                };
                struct page page;
        };
};

В определении структуры folio поля flags, lru и т.д. полностью совпадают с полями page, поэтому они могут быть объединены в union. Таким образом, можно напрямую использовать folio->flags, а не folio->page->flags.

#define page_folio(p)           (_Generic((p),                          \
        const struct page *:    (const struct folio *)_compound_head(p), \
        struct page *:          (struct folio *)_compound_head(p)))

#define nth_page(page,n) ((page) + (n))
#define folio_page(folio, n)    nth_page(&(folio)->page, n)

С первого взгляда page_folio может показаться запутанным, но на самом деле он эквивалентен:

switch (typeof(p)) {
  case const struct page *:
    return (const struct folio *)_compound_head(p);
  case struct page *:
    return (struct folio *)_compound_head(p)));
}

Из макроса page_folio видно, что folio — это, по сути, head page составной страницы. При преобразовании folio в page, folio->page используется для получения head page, а folio_page(folio, n) — для получения tail page.

Зачем нужен folio? В первую очередь, для разработки и эффективности. Если бы folio не было, функция не могла бы определить, является ли current page head page, и вызывала бы _compound_head. Если таких вызовов много на пути выполнения, каждый вызов _compound_head снижает производительность. Но если функция принимает только struct folio *, то этот folio уже указывает на head page, и внутри функции не нужно снова вызывать _compound_head.

Таким образом, основные преимущества:

  1. Уменьшение избыточных вызовов compound_head.
  2. Подсказка разработчику: видя folio, можно считать, что это head page.
  3. Исправление потенциальных ошибок, связанных с tail page.

Принцип уязвимости

В io_uring в функции io_uring_register_buffer есть следующая логика:

image-20240830214419290

Когда количество страниц, переданных из пользовательского пространства, больше 1, io_uring проверяет, является ли переданный buffer объектом folio. Проверка выполняется с помощью page_folio(): получаем head page для page[i], и если head page для page[i] равен head page для page[0], то считается, что они принадлежат одной составной странице.

В обычном случае эта обработка не вызывает проблем, но существует особый случай: если в пользовательском пространстве с помощью mmap отобразить одну и ту же физическую страницу на непрерывное виртуальное адресное пространство, это также удовлетворяет условию проверки, и тогда выполнение попадает в эту ветку:

image-20240830225137539

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

Эксплуатация уязвимости

Распыление cred, а затем с помощью интерфейса выхода за границы (overread/overwrite) изменение uid.

В отличие от эксплойта, найденного в сети, этот эксплойт не зависит от адреса, так как изменяется uid. Если уязвимость присутствует, эксплойт можно использовать.

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <liburing.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <mqueue.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <assert.h>
Скачать инструмент