
Una implementación en Go de PinTheft (CVE-2026-43494)
Port a Go de pintheft - LPE de double-free en zcopy de RDS. Miembro de la clase de vulnerabilidad Dirty Frag.
[!IMPORTANT] Arch Linux es una de las distribuciones más expuestas a esta vulnerabilidad, ya que se encuentra entre las pocas donde el módulo RDS normalmente puede cargarse por defecto.
El error está en rds_message_zcopy_from_user(). La función fija con GUP las páginas de usuario una a una mediante FOLL_GET. Si una página posterior falla (p. ej. una página de guardia PROT_NONE), la ruta de error llama a put_page() sobre las páginas ya fijadas; luego, rds_message_purge() vuelve a llamar a __free_page() sobre ellas porque op_mmp_znotifier fue puesto a NULL pero las entradas op_nents/sg quedaron intactas. Cuando la página todavía tiene otras referencias, __free_page decrementa silenciosamente el contador de referencias. Cada sendmsg fallido roba exactamente una referencia de la primera página.
CONFIG_INIT_ON_ALLOC_DEFAULT_ONFija la página objetivo vía io_uring REGISTER_BUFFERS, que añade GUP_PIN_COUNTING_BIAS (1024) al contador de referencias mediante FOLL_PIN. Roba las 1024 referencias de pin con envíos zcopy fallidos. El contador de referencias de la página es ahora ~1 (solo el mapeo PTE). munmap sigue la ruta normal de __folio_put, que llama a mem_cgroup_uncharge (limpiando memcg_data) antes de liberar. No se dispara ninguna comprobación de bad_page.
io_uring mantiene el struct page* sin procesar en su array bvec sin comprobaciones de vigencia. Una vez que la página se reutiliza como caché de página para un binario suid, IORING_OP_READ_FIXED escribe nuestra carga útil en ella a través de ese puntero colgante.
IORING_REGISTER_CLONE_BUFFERS incrementa imu->refs. Un hijo demonio creado con fork mantiene abierto el fd del ring clonado; io_buffer_unmap ve refs > 1 y omite unpin_user_folio, evitando la corrupción del contador de referencias en la página liberada.
Fija a la CPU 0, vacía las entradas PCP obsoletas antes de liberar - nuestra página queda en la cima cuando el asignador de caché de página la toma.
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
En caso de éxito, abre una shell root vía PTY. El binario en disco no se modifica.
sudo cp /tmp/.backup_pintheft_<pid> /usr/bin/su && sudo chmod u+s /usr/bin/su
CONFIG_RDS=m, CONFIG_RDS_TCP=m (se cargan automáticamente vía SO_RDS_TRANSPORT=2)CONFIG_IO_URING=y con io_uring_disabled=0por cierto, me encanta Arch Linux :)