AF_UNIX MSG_OOB UAF & SKB-based kernel primitives
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
TL;DR
- Linux >=6.9 introduced a flawed
manage_oob()refactor (5aa57d9f2d53) for AF_UNIXMSG_OOBhandling. Stacked zero-length SKBs bypassed the logic that clearsu->oob_skb, so a normalrecv()could free the out-of-band SKB while the pointer remained live, leading to CVE-2025-38236. - Re-triggering
recv(..., MSG_OOB)dereferences the danglingstruct sk_buff. WithMSG_PEEK, the pathunix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()becomes a stable 1-byte arbitrary kernel read; withoutMSG_PEEKthe primitive incrementsUNIXCB(oob_skb).consumedat offset0x44, i.e., adds +4 GiB to the upper dword of any 64-bit value placed at offset0x40inside the reallocated object. - By draining order-0/1 unmovable pages (page-table spray), force-freeing an SKB slab page into the buddy allocator, and reusing the physical page as a pipe buffer, the exploit forges SKB metadata in controlled memory to identify the dangling page and pivot the read primitive into
.data, vmemmap, per-CPU, and page-table regions despite usercopy hardening. - The same page can later be recycled as the top kernel-stack page of a freshly cloned thread.
CONFIG_RANDOMIZE_KSTACK_OFFSETbecomes an oracle: by probing the stack layout whilepipe_write()blocks, the attacker waits until the spilledcopy_page_from_iter()length (R14) lands at offset0x40, then fires the +4 GiB increment to corrupt the stack value. - A self-looping
skb_shinfo()->frag_listkeeps the UAF syscall spinning in kernel space until a cooperating thread stallscopy_from_iter()(viamprotect()over a VMA containing a singleMADV_DONTNEEDhole). Breaking the loop releases the increment exactly when the stack target is live, inflating thebytesargument socopy_page_from_iter()writes past the pipe buffer page into the next physical page. - By monitoring pipe-buffer PFNs and page tables with the read primitive, the attacker ensures the following page is a PTE page, converts the OOB copy into arbitrary PTE writes, and obtains unrestricted kernel read/write/execute. Chrome mitigated reachability by blocking
MSG_OOBfrom renderers (6711812), and Linux fixed the logic flaw in32ca245464e1plus introducedCONFIG_AF_UNIX_OOBto make the feature optional.
Root cause: manage_oob() assumes only one zero-length SKB
unix_stream_read_generic() expects every SKB returned by manage_oob() to have unix_skb_len() > 0. After 93c99f21db36, manage_oob() skipped the skb == u->oob_skb cleanup path whenever it first removed a zero-length SKB left behind by recv(MSG_OOB). The subsequent fix (5aa57d9f2d53) still advanced from the first zero-length SKB to skb_peek_next() without re-checking the length. With two consecutive zero-length SKBs, the function returned the second empty SKB; unix_stream_read_generic() then skipped it without calling manage_oob() again, so the true OOB SKB was dequeued and freed while u->oob_skb still pointed to it.
Secuencia mínima de activación
char byte;
int socks[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
for (int i = 0; i < 2; ++i) {
send(socks[1], "A", 1, MSG_OOB);
recv(socks[0], &byte, 1, MSG_OOB);
}
send(socks[1], "A", 1, MSG_OOB); // SKB3, u->oob_skb = SKB3
recv(socks[0], &byte, 1, 0); // normal recv frees SKB3
recv(socks[0], &byte, 1, MSG_OOB); // dangling u->oob_skb
Primitivas expuestas por unix_stream_recv_urg()
- Lectura arbitraria de 1 byte (repetible):
state->recv_actor()finalmente ejecutacopy_to_user(user, skb_sourced_addr, 1). Si el SKB colgante se realoca en memoria controlada por el atacante (o en un alias controlado como una página de pipe), cadarecv(MSG_OOB | MSG_PEEK)copia un byte desde una dirección kernel arbitraria permitida por__check_object_size()hacia el espacio de usuario sin provocar un crash. MantenerMSG_PEEKactivo preserva el puntero colgante para lecturas ilimitadas. - Escritura restringida: Cuando
MSG_PEEKestá desactivado,UNIXCB(oob_skb).consumed += 1incrementa el campo de 32 bits en el offset0x44. En asignaciones de SKB alineadas a 0x100 esto queda cuatro bytes por encima de una palabra alineada a 8 bytes, convirtiendo la primitiva en un incremento de +4 GiB de la palabra alojada en el offset0x40. Convertir esto en una escritura kernel requiere posicionar un valor sensible de 64 bits en ese offset.
Reasignar la página SKB para lectura arbitraria
- Drenar freelists inamovibles de orden-0/1: Mapear una VMA anónima de solo lectura muy grande y forzar faltas en cada página para forzar la asignación de tablas de páginas (unmovable order-0). Llenar ~10% de la RAM con tablas de páginas asegura que las siguientes asignaciones de
skbuff_head_cachetomen páginas del buddy frescas una vez que se agoten las listas de order-0. - Spray de SKBs y aislar una página de slab: Usar docenas de socketpairs stream y encolar cientos de mensajes pequeños por socket (~0x100 bytes por SKB) para poblar
skbuff_head_cache. Liberar SKBs elegidos para llevar una página de slab objetivo totalmente bajo control del atacante y monitorizar su refcount destruct pagemediante la primitiva de lectura emergente. - Devolver la página de slab al buddy allocator: Liberar cada objeto en la página, luego realizar suficientes asignaciones/liberaciones adicionales para expulsar la página de las listas parciales per-CPU de SLUB y de las listas de páginas per-CPU para que se convierta en una página order-1 en el freelist del buddy.
- Realocar como buffer de pipe: Crear cientos de pipes; cada pipe reserva al menos dos páginas de datos de 0x1000 bytes (
PIPE_MIN_DEF_BUFFERS). Cuando el buddy allocator divide una página order-1, una mitad reutiliza la página SKB liberada. Para localizar qué pipe y qué offset hacen alias conoob_skb, escribir bytes marcadores únicos en SKBs falsos almacenados a lo largo de las páginas de pipe y emitir llamadas repetidas arecv(MSG_OOB | MSG_PEEK)hasta que el marcador sea devuelto. - Forjar un layout SKB estable: Poblar la página de pipe aliada con un
struct sk_bufffalso cuyos punterosdata/heady la estructuraskb_shared_infoapunten a direcciones kernel arbitrarias de interés. Debido a que x86_64 deshabilita SMAP dentro decopy_to_user(), direcciones en espacio de usuario pueden servir como buffers temporales hasta que los punteros del kernel sean conocidos. - Respetar el hardening de usercopy: La copia tiene éxito contra
.data/.bss, entradas vmemmap, rangos vmalloc per-CPU, stacks kernel de otros hilos y páginas de direct-map que no atraviesan límites de folio de orden superior. Lecturas contra.texto caches especializadas rechazadas por__check_heap_object()simplemente retornan-EFAULTsin matar el proceso.
Introspección de allocators con la primitiva de lectura
- Romper KASLR: Leer cualquier descriptor de IDT desde el mapeo fijo en
CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000) y restar el offset conocido del handler para recuperar la base del kernel. - Estado SLUB/buddy: Símbolos globales
.datarevelan bases dekmem_cache, mientras que entradas vmemmap exponen las flags de tipo de cada página, el puntero freelist y la cache propietaria. Escanear segmentos vmalloc per-CPU descubre instancias destruct kmem_cache_cpuasí la próxima dirección de asignación de caches clave (p.ej.,skbuff_head_cache,kmalloc-cg-192) se vuelve predecible. - Tablas de páginas: En lugar de leer
mm_struct(bloqueado por usercopy), recorrer la lista globalpgd_list(struct ptdesc) y emparejar elmm_structactual víacpu_tlbstate.loaded_mm. Una vez conocido elpgdraíz, la primitiva puede atravesar cada tabla de páginas para mapear PFNs para buffers de pipe, tablas de páginas y stacks del kernel.
Reciclar la página SKB como la página superior de la pila del kernel
- Liberar la página de pipe controlada otra vez y confirmar vía vmemmap que su refcount vuelve a cero.
- Inmediatamente asignar cuatro páginas helper de pipe y luego liberarlas en orden inverso para que el comportamiento LIFO del buddy allocator sea determinista.
- Llamar a
clone()para crear un hilo helper; las pilas de Linux son de cuatro páginas en x86_64, así que las cuatro páginas liberadas más recientemente se convierten en su pila, con la última página liberada (la antigua página SKB) en las direcciones más altas. - Verificar vía recorrido de tablas de páginas que el PFN de la parte superior de la pila del hilo helper coincide con el PFN SKB reciclado.
- Usar la lectura arbitraria para observar el layout de la pila mientras se conduce el hilo hacia
pipe_write().CONFIG_RANDOMIZE_KSTACK_OFFSETresta un offset aleatorio de 0x0–0x3f0 (alineado) deRSPpor syscall; escrituras repetidas combinadas conpoll()/read()desde otro hilo revelan cuando el escritor bloquea con el offset deseado. Cuando hay suerte, el argumentobytesderramado porcopy_page_from_iter()(R14) queda en el offset0x40dentro de la página reciclada.
Colocar metadatos SKB falsos en la pila
- Usar
sendmsg()en un socket datagrama AF_UNIX: el kernel copia elsockaddr_undel usuario en unsockaddr_storageresidente en pila (hasta 108 bytes) y los datos ancilares en otro buffer en pila antes de que la syscall bloquee esperando espacio en la cola. Esto permite plantar una estructura SKB falsa precisa en la memoria de la pila. - Detectar cuando la copia terminó suministrando un mensaje de control de 1 byte ubicado en una página de usuario no mapeada;
____sys_sendmsg()la trae por fallo de página, así que un hilo helper que haga polling conmincore()sobre esa dirección aprende cuándo la página destino está presente. - El padding inicializado a cero por
CONFIG_INIT_STACK_ALL_ZEROrellena convenientemente campos no usados, completando un header SKB válido sin escrituras adicionales.
Sincronizar el incremento de +4 GiB con una frag list auto-enlazante
- Forjar
skb_shinfo(fakeskb)->frag_listpara apuntar a un segundo SKB falso (almacenado en memoria de usuario controlada) que tengalen = 0ynext = &self. Cuandoskb_walk_frags()itera esta lista dentro de__skb_datagram_iter(), la ejecución entra en un bucle infinito porque el iterador nunca llega aNULLy el bucle de copia no progresa. - Mantener la syscall recv ejecutándose dentro del kernel dejando que el segundo SKB haga el self-loop. Cuando sea momento de disparar el incremento, simplemente cambiar el puntero
nextdel segundo SKB desde espacio de usuario aNULL. El bucle sale yunix_stream_recv_urg()ejecuta inmediatamenteUNIXCB(oob_skb).consumed += 1una vez, afectando cualquier objeto que ocupe actualmente la página de pila reciclada en el offset0x40.
Bloquear copy_from_iter() sin userfaultfd
- Mapear una VMA anónima RW gigantesca y forzar su fallo completamente.
- Abrir un hueco de una sola página con
madvise(MADV_DONTNEED, hole, PAGE_SIZE)y colocar esa dirección dentro deliov_iterusado parawrite(pipefd, user_buf, 0x3000). - En paralelo, llamar a
mprotect()sobre la VMA completa desde otro hilo. La syscall toma el write lock del mmap y recorre cada PTE. Cuando el escritor de pipe alcanza el hueco, el manejador de fallos de página bloquea en el mmap lock sostenido pormprotect(), pausandocopy_from_iter()en un punto determinista mientras el valorbytesderramado reside en el segmento de pila alojado por la página SKB reciclada.
Convertir el incremento en escrituras arbitrarias de PTE
- Disparar el incremento: Liberar el frag loop mientras
copy_from_iter()está detenido para que el incremento de +4 GiB afecte a la variablebytes. - Desbordar la copia: Una vez que la falla se reanuda,
copy_page_from_iter()cree que puede copiar >4 GiB en la página de pipe actual. Tras llenar los 0x2000 bytes legítimos (dos buffers de pipe), ejecuta otra iteración y escribe los datos de usuario restantes en la página física que sigue al PFN del buffer de pipe. - Alinear la adyacencia: Usando telemetría del allocator, forzar al buddy allocator a colocar una página PTE perteneciente al proceso inmediatamente después de la página objetivo del buffer de pipe (p.ej., alternando entre asignar páginas de pipe y tocar nuevos rangos virtuales para forzar asignación de tablas de páginas hasta que los PFNs se alineen dentro del mismo pageblock de 2 MiB).
- Sobrescribir tablas de páginas: Codificar entradas PTE deseadas en los 0x1000 bytes extra de datos de usuario para que el OOB
copy_from_iter()rellene la página vecina con entradas elegidas por el atacante, concediendo mapeos usuario RW/RWX de memoria física del kernel o reescribiendo entradas existentes para deshabilitar SMEP/SMAP.
Mitigaciones / ideas de hardening
- Kernel: Aplicar
32ca245464e1479bfea8592b9db227fdc1641705(revalida correctamente los SKBs) y considerar deshabilitar AF_UNIX OOB completamente a menos que sea estrictamente necesario víaCONFIG_AF_UNIX_OOB(5155cbcdbf03). Endurecermanage_oob()con chequeos de sanity adicionales (p.ej., buclear hastaunix_skb_len() > 0) y auditar otros protocolos de socket por supuestos similares. - Sandboxing: Filtrar las banderas
MSG_OOB/MSG_PEEKen perfiles seccomp o APIs de broker de más alto nivel (el cambio de Chrome6711812ahora bloqueaMSG_OOBen renderers). - Defensas de allocator: Fortalecer la aleatorización del freelist de SLUB o imponer coloring por página por cache complicaría el reciclado determinista de páginas; limitar el número de buffers de pipe en pipeline también reduce la fiabilidad de la realocación.
- Monitoreo: Exponer vía telemetría la alta tasa de asignación de tablas de páginas o uso anómalo de pipes—este exploit consume grandes cantidades de tablas de páginas y buffers de pipe.
References
- Project Zero – “From Chrome renderer code exec to kernel with MSG_OOB”
- Linux fix for CVE-2025-38236 (
manage_oobrevalidation) - Chromium CL 6711812 – block
MSG_OOBin renderers - Commit adding
CONFIG_AF_UNIX_OOBprompt
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


