
Реализация PinTheft на Go (CVE-2026-43494)
Go-порт pintheft — LPE через double-free в RDS zcopy. Относится к классу уязвимостей Dirty Frag.
[!IMPORTANT] Arch Linux — один из наиболее подверженных данной уязвимости дистрибутивов, поскольку относится к немногим, где модуль RDS обычно может быть загружен по умолчанию.
Ошибка находится в rds_message_zcopy_from_user(). Функция пиннит пользовательские страницы через GUP по одной при помощи FOLL_GET. Если более поздняя страница вызывает page fault (например, страница-ограждение PROT_NONE), ветка обработки ошибки вызывает put_page() для уже закреплённых страниц, а затем rds_message_purge() снова вызывает для них __free_page(), поскольку op_mmp_znotifier был обнулён, но записи op_nents/sg остались нетронутыми. Когда у страницы всё ещё есть другие ссылки, __free_page молча уменьшает счётчик ссылок. Каждый завершающийся с ошибкой sendmsg крадёт ровно одну ссылку с первой страницы.
CONFIG_INIT_ON_ALLOC_DEFAULT_ONЗакрепите целевую страницу через io_uring REGISTER_BUFFERS, который добавляет GUP_PIN_COUNTING_BIAS (1024) к счётчику ссылок через FOLL_PIN. Украдите все 1024 pin-ссылки с помощью завершающихся с ошибкой zcopy-отправок. Теперь счётчик ссылок страницы равен ~1 (только PTE-отображение). munmap проходит обычным путём __folio_put, который перед освобождением вызывает mem_cgroup_uncharge (очищая memcg_data). Проверка bad_page не срабатывает.
io_uring хранит сырой struct page* в своём массиве bvec без проверок живости. После того как страница переиспользована как page cache для suid-бинарника, IORING_OP_READ_FIXED записывает в неё нашу нагрузку через этот висячий указатель.
IORING_REGISTER_CLONE_BUFFERS увеличивает imu->refs. Дочерний процесс-демон после fork удерживает открытым fd клонированного кольца — io_buffer_unmap видит refs > 1 и пропускает unpin_user_folio, предотвращая порчу счётчика ссылок освобождённой страницы.
Закрепитесь на CPU 0, сбросьте устаревшие записи PCP перед освобождением — наша страница окажется в начале списка, когда аллокатор page cache захватит её.
REGISTER_BUFFERS(+1024) -> CLONE_BUFFERS(imu->refs=2) ->
daemon holds clone -> steal 1024 refs -> evict page cache ->
drain PCP -> munmap(free) -> pread(reclaim) ->
READ_FIXED(overwrite) -> verify -> exec -> root
go build -o pintheft-go .
./pintheft-go
При успехе открывается root-шелл через PTY. Бинарник на диске не изменяется.
sudo cp /tmp/.backup_pintheft_<pid> /usr/bin/su && sudo chmod u+s /usr/bin/su
CONFIG_RDS=m, CONFIG_RDS_TCP=m (автозагружаются через SO_RDS_TRANSPORT=2)CONFIG_IO_URING=y с io_uring_disabled=0кстати, я обожаю Arch Linux :)