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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2026-43499-S26 — Эксплойт ядра Android GKI 6.12 для CVE-2026-43499, объединяющий ошибку отката rt_mutex с перезаписью стека pselect для получения root на устройствах Samsung и Pixel. | Kitploit
Инструменты/GitHubGitHub/sammyenigma/cve-2026-43499-s26
Безопасность AndroidПовышение привилегийКриминалистика памятиМеханизмы персистентностиЭксплуатацияОбратная инженерияШелл-кодПост-эксплуатацияМобильная безопасность

Популярное

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

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

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

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

Смотреть все инструменты →
Поделиться
Разработка Полезной Нагрузки
Эксплуатация Бинарных Файлов
GitHubsammyenigma/cve-2026-43499-s26

CVE-2026-43499-S26

Эксплойт ядра Android GKI 6.12 для CVE-2026-43499, объединяющий ошибку отката rt_mutex с перезаписью стека pselect для получения root на устройствах Samsung и Pixel.

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

CVE-2026-43499 — эксплойт ядра Android GKI 6.12 pselect / configfs

Эксплойт ядра Linux для линейки Android GKI 6.12 (устройства Samsung и Pixel), нацеленный на CVE-2026-43499: ошибочный откат remove_waiter() в rt_mutex_start_proxy_lock(), который использует current вместо waiter::task, оставляя pi_blocked_on ожидающего указывающим на его (позже извлечённый из стека) rt_mutex_waiter в стеке ядра. В сочетании с перезаписью стека fd_set через pselect() и обходом поддельного rb_tree, управляемым потребителем через sched_setattr, это даёт детерминированный примитив записи в ядро и полный root.

Эксплойт работает как разделяемая библиотека LD_PRELOAD (preload.so) и устанавливает root-демон su плюс обои как пост-root артефакты.

Статус: активная разработка. Цель m1q (ZF1) — основной фокус bring-up. pipei tmp_page-uname bootstrap — текущий активный маршрут: обход доказанно чист на устройстве (сборка #33: посеянные rt_mutex-регионы для каждого потомка), и полный перебор 168 кандидатов в процессе. Маршрут configfs CFI — тупик на m1q (Rust ashmem — нет инжектируемого слота fops) и остаётся маршрутом для целей C-ashmem. Смещения для каждой цели различаются по устройству — проверяйте их по фактическому бинарнику ядра, прежде чем доверять им.

Уязвимый примитив (CVE-2026-43499)

Когда FUTEX_CMP_REQUEUE_PI переставляет ожидающего в очередь и обход цепочки обнаружения взаимоблокировок при перестановке возвращает -EDEADLK, __rt_mutex_start_proxy_lock() выполняет откат через remove_waiter() (rtmutex.c:1535). Откат корректно удаляет ожидающего из дерева ожидания, но очищает pi_blocked_on вызывающего перестановку, а не waiter->task->pi_blocked_on. pi_blocked_on ОЖИДАЮЩЕГО остаётся висящим на его стековом rt_mutex_waiter, который извлекается из стека, как только futex истекает по таймауту.

После таймаута область стека ядра ожидающего переиспользуется: core_sys_select() копирует три fd_set в этот стековый буфер (путь стека nfds < 344 на ZF1). Специально сформированный массив слов fd_set воссоздаёт поддельный rt_mutex_waiter / поддельную задачу / поддельный rt_mutex (с корнем rb_tree, указатели которого контролируются). Затем поток-потребитель вызывает sched_setattr_tid(waiter) → rt_mutex_adjust_pi() → rb_erase_cached, что производит запись контролируемого значения по произвольному адресу.

Весь примитив требует цикла в PI-цепочке: владелец удерживает f_pi_target и также блокируется на f_pi_chain (удерживаемом ожидающим), поэтому обход цепочки попадает в owner → chain → waiter → target → owner и завершается ошибкой -EDEADLK. Удаление цикла заставляет перестановку вернуть успех с нулевым эффектом в ядре.

Цепочка эксплуатации

  1. Slide (KASLR) — утечка базы ядра. Два маршрута:
    • tracefs sched_blocked_reason (основной на m1q, доказан на устройстве): сохранённый PC возврата заблокированного kworker (stack_trace_save_tsk) читается из кольцевого буфера и сравнивается со вкомпилированным смещением worker_thread. Выполняется до использования любых слов маршрута boot_id, чтобы цели записи через data-alias (data_addr() = p0 alias + slide_p0_offset) оставались корректными при ненулевом slide.
    • маршрут boot_id pselect (запасной, не зависит от slide): запись pselect помещает SLIDE_LOGGERS_0_1 в данные sysctl boot_id через alias линейного отображения; утёкшее значение восстанавливает stext. Отложен на m1q: его цель W1 (SLIDE_RANDOM_BOOT_ID_DATA_OFF, младшая физическая RAM) имеет зависящую от страницы записываемость — slide перемещает цель через границы страниц при каждой загрузке, поэтому ~6/7 запусков дают сбой. Используется только явно через SLIDE_FORCE_BOOTID (тест механизма) с перенаправлением tree_pc/tree_left на заспреенную страницу.
  2. KernelSnitch — спрей объектов размером с mm_struct и использование коллизии futex-хэша для обнаружения в куче, что утекает адрес страницы кучи ядра, используемый как база спрея поддельных объектов. Ожидающие отсоединяются (не присоединяются) при очистке, чтобы избежать OOM стека ядра, который приводил к краху сборок до #26.

Форма обхода (m1q, доказано на устройстве)

Слова fd_set pselect воссоздают поддельный rt_mutex_waiter в стеке ядра ожидающего; sched_setattr_tid(waiter) потока-потребителя обходит его через rt_mutex_adjust_pi(). Карта слов ZF1 (проверено по дизассемблеру): PSELECT_WAITER_WORD_SHIFT = 0, слово 12 = task (@+0x50), слово 13 = lock (@+0x58), слово 14 = wake_state (@+0x60 = 3). С посеянным поддельным rt_mutex обход завершается чисто: rb_erase W1 *(tree_left)=tree_pc / W2 *(tree_pc&~3+8)=tree_left из [7] — единственные записи в ядро; [11] (setprio / dequeue_pi) и пробуждение [9] оба пропускаются (локальный поддельный ожидающий с приоритетом 100 удерживает стековый узел вне top-waiter). Каждое завершение обхода, входившее в него, начиная со сборки #19 выживает на устройстве; детерминированные крахи до этого были ошибкой размещения 8-байтовой полезной нагрузки (SKB_DATA_DELTA), а не сбоем тела обхода.

Стадия 3 (мост pgd-swap, только статика/сборка)

Опциональная фаза STAGE3=1, которая форкает дочерний мост, подменяет его mm->pgd на подготовленную поддельную таблицу страниц (3 уровня: PGD→L1→L2, RX-лист цикла + RW-лист стека) и позволяет потомку выполнить ассемблерный блоб только из регистров, который патчит его собственный cred через окно физического сканирования 2MB — физическое чтение/запись всей RAM без примитивов configfs/pipe. В настоящее время подключено только в цель m1q и не запускалось на устройстве.

Поддерживаемые цели

41 цель в src/targets/<codename>-<build>/, каждая требует как минимум target.h (смещения символов ядра, KIMAGE_TEXT_BASE, база прямого отображения, смещения структур). Цели Pixel (comet, tokay, tegu, caiman, komodo, frankel, mustang, rango, stallion, blazer) переопределяют общие исходники; цели Samsung (m1q-*) добавляют специфичную для устройства логику.

root@kitploit:~
make list-projects   # полный список

Раскладки структур ядра различаются по устройству — смещения должны быть проверены по фактическому бинарнику ядра / kallsyms для каждой цели.

Сборка

Выход: build/<PROJECT>/bin/preload.so (разделяемая библиотека, загружаемая через LD_PRELOAD).

root@kitploit:~
# Проект по умолчанию
CC=clang make

# Конкретная цель устройства (по умолчанию: blazer-CP2A.260605.012)
CC=clang make PROJECT=m1q-BP4A.251205.006

# Без CC=clang: использует NDK, если найден ($NDK_ROOT / $ANDROID_NDK_HOME /
# $ANDROID_NDK_ROOT), иначе host clang + Android sysroot
make PROJECT=m1q-BP4A.251205.006

# Показать конфигурацию сборки
make info

# Очистить
make clean

Сборка встраивает PIE-бинарник su_daemon (src/su_daemon.c, собирается в build/embed/su_daemon_aarch64_pie) и assets/wallpaper.webp в preload.so через src/su_blob.S / src/wallpaper_blob.S.

Запуск на устройстве

Загрузите выходные файлы сборки в /data/local/tmp, затем запустите эксплойт под LD_PRELOAD. Запускайте БЕЗ tee на живой цели bring-up — tee буферизует свой stdio и теряет хвост лога при панике ядра (stdout эксплойта небуферизован, поэтому перенаправление прямо в файл сохраняет каждую строку):

root@kitploit:~
adb push build/m1q-BP4A.251205.006/bin/preload.so /data/local/tmp/preload.so
adb push build/embed/su_daemon_aarch64_pie /data/local/tmp/su_daemon_aarch64_pie
adb shell "chmod 755 /data/local/tmp/preload.so /data/local/tmp/su_daemon_aarch64_pie"

adb shell "LD_PRELOAD=/data/local/tmp/preload.so \
  /data/local/tmp/su_daemon_aarch64_pie \
  > /data/local/tmp/output.log 2>&1"

При успехе демон слушает на /data/local/tmp/temp_su.sock, а su установлен под /apex/com.android.virt/bin.

Перебор pipei на m1q (текущий bring-up)

tools/run_pipei.sh загружает бинарник, проверяет его sha256 на устройстве (устаревший preload.so даёт молча нечитаемые вердикты — лестница однажды запускала сборку < #22, чей формат лога отличается), затем запускает полный перебор 168 кандидатов, разбитый на 3x56, чтобы паника теряла не более одного фрагмента (~10 мин). stdout эксплойта стримится напрямую через adb (без перенаправления на устройстве — tee на стороне устройства теряет хвост при панике); хостовая копия каждого фрагмента хранится в /tmp/m1q_pipei_chunk{1..3}_stream.log:

root@kitploit:~
sh tools/run_pipei.sh

PASS = любой фрагмент показывает tmp_page uname changed + after uname='CatOS. PIPEI_CHILD_REGIONS должен быть установлен как минимум в размер фрагмента, иначе rt_mutex-регионы для каждого потомка остаются не посеянными и перебор паникует на кандидате 0. Полное покрытие также требует TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES=1 — без него перебирается только одна страница освобождённого mm-блока из 8 страниц. run_pipei_slim.sh — вариант диагностики одного кандидата; run_m1q_ladder.sh — батарея обхода/изоляции (оракул B16-2, механизм slide, шаги перебора).

Конфигурация времени выполнения (переменные окружения)

Структура проекта

root@kitploit:~
exploit/
  Makefile
  assets/                     # wallpaper.webp (встроенный пост-root артефакт)
  src/
    main.c                    # точка входа run_exploit() (общая)
    preload.c                 # конструктор LD_PRELOAD, установка su/обоев
    util.c                    # трансляция адресов, подготовка страниц, помощники configfs
    pipe.c                    # физическое чтение/запись через pipe
    root.c                    # патчинг cred / обход задач
    slide.c                   # slide KASLR (маршруты tracefs + boot_id)
    fops.c                    # повреждение pselect + манипуляция fops
    faketables.c              # построители поддельных таблиц страниц стадии 3 (3 уровня)
    stage3.c                  # дочерний мост pgd-swap (no-op, если не настроен)
    stage3_loop.S, stage3_poll.S  # блоб моста + трамплин входа
    su_daemon.c               # root-демон (компилируется как отдельный PIE)
    su_blob.S, wallpaper_blob.S   # встроенные бинарники
    offset.h                  # подтягивает target.h для каждой цели через -DTARGET_CONFIG_H
    kernelsnitch/             # библиотека утечки mm_struct через futex-хэш
    targets/<codename>-<build>/    # target.h для каждого устройства + опциональные переопределения
poc/poc.c                     # автономный POC (отдельно от эксплойта)

Цели переопределяют общие исходники через механизм pick_src в Makefile: если src/targets/<PROJECT>/<file>.c существует, он используется вместо общего src/<file>.c; иначе используется общая версия.

Заметки и подводные камни

  • Никогда не удаляйте цикл PI-цепочки. Примитив записи зависит от того, что перестановка завершается ошибкой -EDEADLK (errno 35). Отсутствующий цикл проявляется как success=1 с нулевым эффектом в ядре.
  • Ожидающий должен удерживать блокировку PI-цепочки во время pselect. Освобождение её первым позволяет владельцу проснуться и устроить гонку с обходом поддельного rb_tree → паника.
  • m1q: PSELECT_WAITER_WORD_SHIFT должен быть 0 (глубины стека ZF1 проверены по ELF). Другие цели всё ещё по умолчанию используют 1 — проверяйте по устройству, прежде чем доверять им.
  • p0_data_alias() использует DIRECT_MAP_BASE (линейное отображение), которое отличается от P0_PAGE_OFFSET на ZF1. Data-alias для разыменовываемых объектов ядра должны использовать линейное отображение.
  • pr_error() фатален (exit(-1)), в отличие от макросов предупреждения/информации.
  • Реконструкция slide использует SLIDE_LOGGERS_0_1, а не SLIDE_NFULNL_LOGGER; слово — это нерандомизированный alias линейного отображения, поэтому маршрут boot_id не зависит от slide (используйте его только при slide=0 — сначала запустите утечку tracefs).

Отказ от ответственности

Это эксплойт-код для устройств Android, предназначенный для исследований в области безопасности, авторизованного тестирования уязвимостей и защитной работы на оборудовании, которым вы владеете или на которое у вас есть явное разрешение на тестирование. Неправомерное использование на устройствах, которыми вы не владеете, незаконно. Автор не несёт ответственности за превращённые в кирпич устройства, превращённые в кирпич ядра или аннулированные гарантии.

Скачать инструмент
mm_struct
  • Основной маршрут — pipei tmp_page bootstrap (m1q, текущий фокус) — исходный маршрут configfs CFI — тупик на m1q: у Rust ashmem нет инжектируемого статического слота write_iter, а кучевый объект ASHMEM_SET_NAME — это KVec, чья раскладка не совпадает с private_data configfs_bin_write_iter. m1q переключается на запись в kmalloc: возвращает утёкший mm-блок порядка 3 как объекты pipe_inode_info и использует запись pselect W1 (*(tree_left) = tree_pc) для перезаписи слота tmp_page кандидата (+0x90) страницей пространства имён UTS (init_uts_ns). Веерная запись помещает маркерное имя по смещению sysname, и uname() сообщает "CatOS" — проверяемая запись в ядро без зависимости от статических объектов. Цели C-ashmem сохраняют путь configfs.
  • Pipe physrw — подделка страниц буфера pipe в заспреенной странице ядра для физического чтения/записи: патчинг cred, отключение SELinux и прямая манипуляция памятью ядра.
  • Root — патчинг cred корневого потомка (uid/gid/caps/SELinux SID), затем preload.c устанавливает встроенный демон su (смонтированный через tmpfs в /apex/com.android.virt/bin, плюс варианты в пространстве имён adbd и локальные) и меняет обои.
  • ПеременнаяПо умолчаниюНазначение
    PSELECT_ROUTE_SHIFTвремя компиляцииA/B сдвиг слова fd_set для основного маршрута (m1q)
    SLIDE_SHIFTвремя компиляцииA/B сдвиг слова fd_set для маршрута slide
    PSELECT_ROUTE_DELAY_USEC50000Задержка потребителя перед sched_setattr (должна быть > 0)
    SLIDE_CONSUME_USEC, SLIDE_ENTER_DELAY_USEC, SLIDE_CONSUMER_CORE—Тайминг/привязка маршрута slide
    SKIP_SLIDE0Использовать запасной вариант прямого отображения (slide = 0)
    SLIDE_ONLY0Выполнить только slide KASLR и выйти
    SLIDE_P0_OFFSET, SLIDE_BOOTID_OFF—Переопределить смещения slide p0-alias / boot_id
    SLIDE_FORCE_BOOTID0Принудительно использовать маршрут boot_id pselect после slide tracefs (тест механизма)
    KSNITCH_COLLISIONS4Количество коллизий KernelSnitch
    STAGE30Включить фазу дочернего моста pgd-swap (m1q)
    STAGE3_DRYRUN0Подготовить/проверить таблицы, затем прервать до подмены
    PAGE_RECLAIM_SENDS, PSELECT_SIMPLE_LAYOUT—Настройки спрея/раскладки fd_set
    TMP_PAGE_UNAME0Запустить pipei tmp_page-uname bootstrap на m1q
    TMP_UNAME_PIPEI_SWEEP, TMP_UNAME_PIPEI_ALIGN_ORDER, TMP_UNAME_PIPEI_SPLIT_ORDER_PAGES—Режим перебора; split_order перебирает все 8 страниц освобождённого mm-блока
    TMP_UNAME_PIPEI_SLOT, TMP_UNAME_PIPEI_SLOT_CANDIDATES—Диапазон перебора (начало фрагмента / количество)
    PIPEI_CHILD_REGIONS0Посеянные rt_mutex-регионы для каждого потомка; ДОЛЖНО быть >= фрагмента перебора, иначе не посеянные регионы паникуют на кандидате 0
    PIPEI_RECLAIM_COUNT, PIPEI_DRAIN_COUNT—Возврат/слив пар pipe; увеличьте при промахе возврата
    PIPEI_SPONGE_MB3072Губка-флуд порядка 0, удерживаемая во время возврата (сборка #29)
    PSELECT_W0_PRIO_OVERRIDE130Приоритет top-waiter; инертен с посеянными регионами (устаревшая защита от устаревшего дерева, только A/B)
    PSELECT_SKB_DELTA_OVERRIDE0A/B смещение полезной нагрузки внутри страницы (только если устройство отличается от -0xe78)
    TMP_UNAME_NAME, TMP_UNAME_HOLD_SEC"CatOS", 5Маркерное имя / время удержания после успеха
  • SKB_DATA_DELTA равен -0xe78 (измерено на устройстве, сборка #19) — реальный frag head skb на 8 байт меньше, чем выведенный из дизассемблера -0xe80; эта 8-байтовая ошибка вызывала каждый детерминированный крах pselect со сборок #6-18 (вход в обход → разыменование мусора). Общее значение GKI 6.12; копии util.c для каждой цели несут A/B-ручку окружения SKB_DATA_DELTA. НЕ накладывайте PSELECT_SKB_DELTA_OVERRIDE поверх вкомпилированной константы.
  • Тело обхода чисто; ошибкой было размещение. С посеянной полезной нагрузкой по смоделированным смещениям обход цепочки завершается без сбоев на устройстве. Крах при входе в обход означает, что память по адресам слов fd — это НЕ полезная нагрузка (гонка возврата/размещения), а не ошибка модели.
  • m1q: маршрут configfs CFI — тупик (Rust ashmem) — нет инжектируемого статического слота fops, и оболочка KVec ASHMEM_SET_NAME не совпадает с configfs_bin_write_iter. Используйте pipei tmp_page bootstrap. Защиты PSELECT_WRITE_RIGHT_SPRAYED / PSELECT_PI_TOP_TASK_SELF в target.h m1q перенаправляют записи [11] обхода прочь от RO-статических fops.
  • rt_mutex-регионы для каждого потомка (сборка #33): каждый дочерний процесс перебора перенаправляет слово fd w13 в свой собственный посеянный регион (payload+0x6000 + idx*0x40) внутри возвращённой страницы. Переборы с общим fake_lock паниковали на кандидате ~9-12 (переработанные устаревшие узлы стека ядра), а пустые регионы сборки #32 паниковали на кандидате 0 (пробуждение [9] owner==NULL на fake_task, заполненном 0x41). PIPEI_CHILD_REGIONS должен быть установлен, иначе регионы остаются не посеянными.
  • slide boot_id pselect на m1q отложен — его цель W1 — младшая RAM, чья записываемость страниц варьируется при каждой загрузке; tracefs — основной и измеряет slide при каждом запуске.