AF_UNIX MSG_OOB UAF & SKB-based kernel primitives
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
TL;DR
- Linux >=6.9 ha introdotto un refactor difettoso di
manage_oob()(5aa57d9f2d53) per la gestione AF_UNIXMSG_OOB. SKB a lunghezza zero impilati bypassavano la logica che azzerau->oob_skb, quindi una normalerecv()poteva liberare lo SKB out-of-band mentre il puntatore restava valido, portando a CVE-2025-38236. - Riattivare
recv(..., MSG_OOB)dereferenzia ilstruct sk_buffpendente. ConMSG_PEEK, il percorsounix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()diventa una lettura kernel arbitraria stabile di 1 byte; senzaMSG_PEEKil primitivo incrementaUNIXCB(oob_skb).consumedall’offset0x44, ossia aggiunge +4 GiB al dword alto di qualsiasi valore a 64 bit posizionato all’offset0x40dentro l’oggetto riallocato. - Svuotando pagine unmovable di order-0/1 (page-table spray), forzando il free di una pagina slab SKB nel buddy allocator e riutilizzando la pagina fisica come pipe buffer, l’exploit forgia i metadata dello SKB in memoria controllata per identificare la pagina dangling e pivotare la primitive di lettura verso
.data, vmemmap, per-CPU e regioni di page-table nonostante il usercopy hardening. - La stessa pagina può poi essere riciclata come pagina superiore dello kernel-stack di un thread appena clonata.
CONFIG_RANDOMIZE_KSTACK_OFFSETdiventa un oracolo: sondando il layout dello stack mentrepipe_write()è bloccata, l’attaccante aspetta che la lunghezza spillata dicopy_page_from_iter()(R14) finisca all’offset0x40, poi scatta l’incremento di +4 GiB per corrompere il valore sullo stack. - Una
skb_shinfo()->frag_listin loop mantiene la syscall UAF in esecuzione nello spazio kernel finché un thread cooperante non bloccacopy_from_iter()(viamprotect()su una VMA contenente un singolo holeMADV_DONTNEED). Rompere il loop rilascia l’incremento esattamente quando l’obiettivo sullo stack è live, gonfiando l’argomentobytescosì checopy_page_from_iter()scriva oltre la pagina del pipe buffer nella pagina fisica successiva. - Monitorando i PFN del pipe-buffer e le page table con la primitive di lettura, l’attaccante si assicura che la pagina successiva sia una PTE page, converte la copia OOB in scritture arbitrarie di PTE e ottiene accesso kernel R/W/X illimitato. Chrome ha mitigato la raggiungibilità bloccando
MSG_OOBdai renderer (6711812), e Linux ha corretto la logica in32ca245464e1aggiungendoCONFIG_AF_UNIX_OOBper rendere la feature opzionale.
Causa principale: manage_oob() presuppone un solo zero-length SKB
unix_stream_read_generic() si aspetta che ogni SKB restituito da manage_oob() abbia unix_skb_len() > 0. Dopo 93c99f21db36, manage_oob() saltava il percorso di cleanup skb == u->oob_skb ogni volta che rimuoveva per primo uno SKB a lunghezza zero lasciato da recv(MSG_OOB). La successiva correzione (5aa57d9f2d53) avanzava comunque dallo primo zero-length SKB con skb_peek_next() senza ricontrollare la lunghezza. Con due SKB consecutivi a lunghezza zero, la funzione restituiva il secondo SKB vuoto; unix_stream_read_generic() lo saltava senza richiamare manage_oob() di nuovo, quindi il vero OOB SKB veniva estratto dalla coda e liberato mentre u->oob_skb continuava a puntarci.
Sequenza minima di trigger
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
Primitive esposte da unix_stream_recv_urg()
- 1-byte arbitrary read (repeatable):
state->recv_actor()esegue infinecopy_to_user(user, skb_sourced_addr, 1). Se l’SKB pendente viene riallocato in memoria controllata dall’attaccante (o in un alias controllato come una pipe page), ognirecv(MSG_OOB | MSG_PEEK)copia un byte da un indirizzo kernel arbitrario consentito da__check_object_size()nello spazio utente senza causare il crash. MantenereMSG_PEEKimpostato preserva il puntatore pendente per letture illimitate. - Constrained write: Quando
MSG_PEEKè disabilitato,UNIXCB(oob_skb).consumed += 1incrementa il campo a 32 bit all’offset0x44. Su allocazioni SKB allineate a 0x100 questo si trova quattro byte sopra una parola allineata a 8 byte, convertendo la primitiva in un incremento di +4 GiB della parola ospitata all’offset0x40. Trasformare ciò in una scrittura kernel richiede il posizionamento di un valore sensibile a 64 bit in quell’offset.
Riallocare la pagina SKB per lettura arbitraria
- Svuotare le freelist unmovable di ordine 0/1: Mappa un grande VMA anonimo read-only e provoca fault su ogni pagina per forzare l’allocazione delle page-table (order-0 unmovable). Riempire ~10% della RAM con page table garantisce che le successive allocazioni di
skbuff_head_cacheprendano nuove pagine dal buddy una volta esaurite le liste di ordine-0. - Spray di SKB e isolamento di una slab page: Usa dozzine di stream socketpair e metti in coda centinaia di piccoli messaggi per socket (~0x100 byte per SKB) per popolare
skbuff_head_cache. Free alcune SKB scelte per portare una pagina slab bersaglio completamente sotto controllo dell’attaccante e monitora il suo refcount distruct pagetramite la primitiva di lettura emersa. - Restituire la slab page al buddy allocator: Free ogni oggetto sulla pagina, poi esegui allocazioni/free addizionali sufficienti a spingere la pagina fuori dalle liste partial per-CPU di SLUB e dalle per-CPU page lists in modo che diventi una pagina order-1 nella freelist del buddy.
- Riallocare come pipe buffer: Crea centinaia di pipe; ogni pipe riserva almeno due pagine dati da 0x1000 byte (
PIPE_MIN_DEF_BUFFERS). Quando il buddy allocator splitta una pagina order-1, una metà riusa la pagina SKB liberata. Per individuare quale pipe e quale offset aliasanooob_skb, scrivi byte marker unici in fake SKB posti attraverso le pipe pages e emetti ripetute chiamaterecv(MSG_OOB | MSG_PEEK)finché non viene restituito il marker. - Falsificare un layout SKB stabile: Popola la pipe page aliasata con un fake
struct sk_buffi cui puntatoridata/heade la strutturaskb_shared_infopuntano ad indirizzi kernel arbitrari di interesse. Poiché x86_64 disabilita SMAP dentrocopy_to_user(), indirizzi in user-mode possono servire come buffer di staging finché i puntatori kernel non sono noti. - Rispettare l’hardening usercopy: La copy ha successo contro
.data/.bss, vmemmap entries, intervalli vmalloc per-CPU, stack kernel di altri thread e pagine direct-map che non attraversano confini di folio di ordine superiore. Le letture contro.texto cache specializzate rifiutate da__check_heap_object()semplicemente ritornano-EFAULTsenza killare il processo.
Ispezionare gli allocator con la primitiva di lettura
- Break KASLR: Leggi qualsiasi descrittore IDT dal fixed mapping a
CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000) e sottrai l’offset del handler noto per recuperare la base del kernel. - Stato SLUB/buddy: Simboli globali
.datarivelano le basi dikmem_cache, mentre le vmemmap entries espongono i flag di tipo di ogni pagina, il puntatore freelist e la cache proprietaria. Scannerizzare i segmenti vmalloc per-CPU scopre istanze distruct kmem_cache_cpucosì l’indirizzo dell’allocazione successiva di cache chiave (es.,skbuff_head_cache,kmalloc-cg-192) diventa predicibile. - Page tables: Invece di leggere
mm_struct(bloccato da usercopy), cammina il globalpgd_list(struct ptdesc) e abbina l’attualemm_structtramitecpu_tlbstate.loaded_mm. Una volta noto il rootpgd, la primitiva può attraversare tutte le page table per mappare i PFN di pipe buffers, page table e stack kernel.
Riciclare la pagina SKB come la pagina superiore dello stack del kernel
- Free la pipe page controllata di nuovo e conferma via vmemmap che il suo refcount ritorni a zero.
- Alloca immediatamente quattro pagine pipe helper e poi freele in ordine inverso in modo che il comportamento LIFO del buddy allocator sia deterministico.
- Chiama
clone()per spawnare un thread helper; gli stack Linux sono quattro pagine su x86_64, quindi le quattro pagine più recentemente freeate diventano il suo stack, con l’ultima pagina freeata (l’ex pagina SKB) agli indirizzi più alti. - Verifica tramite page-table walk che il PFN dello stack superiore del thread helper corrisponda al PFN della SKB riciclata.
- Usa la lettura arbitraria per osservare il layout dello stack mentre guidi il thread dentro
pipe_write().CONFIG_RANDOMIZE_KSTACK_OFFSETsottrae un valore casuale 0x0–0x3f0 (allineato) daRSPper syscall; scritture ripetute combinate conpoll()/read()da un altro thread rivelano quando lo writer si blocca con l’offset desiderato. Quando fortunati, l’argomentobytesdicopy_page_from_iter()(R14) si trova a offset0x40dentro la pagina riciclata.
Posizionare metadati SKB falsi sullo stack
- Usa
sendmsg()su una AF_UNIX datagram socket: il kernel copia ilsockaddr_unutente in unosockaddr_storageresidente sullo stack (fino a 108 byte) e i dati ancillari in un altro buffer on-stack prima che la syscall si blocchi in attesa di spazio in coda. Questo permette di piantare una struttura fake SKB precisa nella memoria dello stack. - Rileva quando la copy è terminata fornendo un controllo (control message) di 1 byte situato in una pagina utente non mappata;
____sys_sendmsg()la fa faultare, quindi un thread helper che fa polling sumincore()su quell’indirizzo apprende quando la pagina di destinazione è presente. - L’azzeramento del padding dovuto a
CONFIG_INIT_STACK_ALL_ZEROriempie convenientemente i campi inutilizzati, completando un header SKB valido senza scritture aggiuntive.
Temporizzare l’incremento +4 GiB con una frag list auto-iterante
- Forgia
skb_shinfo(fakeskb)->frag_listper puntare a una seconda fake SKB (memorizzata in memoria utente controllata) che halen = 0enext = &self. Quandoskb_walk_frags()itera questa lista dentro__skb_datagram_iter(), l’esecuzione si blocca indefinitamente perché l’iteratore non raggiunge maiNULLe il loop di copy non fa progressi. - Mantieni la syscall recv in esecuzione dentro il kernel lasciando la seconda fake SKB auto-iterare. Quando è il momento di sparare l’incremento, semplicemente cambia da user-space il puntatore
nextdella seconda SKB da user aNULL. Il loop esce eunix_stream_recv_urg()esegue immediatamenteUNIXCB(oob_skb).consumed += 1una sola volta, influenzando qualunque oggetto occupi attualmente la pagina stack riciclata all’offset0x40.
Bloccare copy_from_iter() senza userfaultfd
- Mappa un grande VMA anonimo RW e fallo faultare completamente.
- Crea un buco di una singola pagina con
madvise(MADV_DONTNEED, hole, PAGE_SIZE)e inserisci quell’indirizzo dentro l’iov_iterusato perwrite(pipefd, user_buf, 0x3000). - In parallelo, chiama
mprotect()sull’intero VMA da un altro thread. La syscall prende la mmap write lock e cammina ogni PTE. Quando il writer della pipe raggiunge il buco, il page fault handler si blocca sulla mmap lock tenuta damprotect(), mettendo in pausacopy_from_iter()in un punto deterministico mentre il valorebytesspillato risiede sul segmento di stack ospitato dalla pagina SKB riciclata.
Trasformare l’incremento in scritture arbitrarie di PTE
- Spara l’incremento: Rilascia il frag loop mentre
copy_from_iter()è in pausa in modo che l’incremento di +4 GiB colpisca la variabilebytes. - Overflow della copy: Una volta che il fault riprende,
copy_page_from_iter()crede di poter copiare >4 GiB nella pipe page corrente. Dopo aver riempito i legittimi 0x2000 byte (due pipe buffer), esegue un’altra iterazione e scrive i rimanenti dati utente nella qualunque pagina fisica segua il PFN del pipe buffer. - Pianificare l’adiacenza: Usando la telemetria dell’allocator, forza il buddy allocator a posizionare una page PTE posseduta dal processo immediatamente dopo la pagina del pipe buffer bersaglio (es., alternando allocazioni di pipe page e toccando nuovi range virtuali per triggerare l’allocazione di page-table finché i PFN non si allineano dentro lo stesso 2 MiB pageblock).
- Sovrascrivere le page table: Codifica le voci PTE desiderate nei 0x1000 byte extra dei dati utente così che la OOB
copy_from_iter()riempia la pagina adiacente con voci scelte dall’attaccante, garantendo mappature user RW/RWX di memoria fisica kernel o riscrivendo voci esistenti per disabilitare SMEP/SMAP.
Mitigazioni / idee di hardening
- Kernel: Applica
32ca245464e1479bfea8592b9db227fdc1641705(revalida correttamente gli SKB) e considera la disabilitazione completa di AF_UNIX OOB a meno che non sia strettamente necessario tramiteCONFIG_AF_UNIX_OOB(5155cbcdbf03). Induriremanage_oob()con controlli di sanità addizionali (es., loop fino aunix_skb_len() > 0) e auditare altri protocolli socket per assunzioni simili. - Sandboxing: Filtrare i flag
MSG_OOB/MSG_PEEKnei profili seccomp o nelle API broker di livello superiore (cambiamento Chrome6711812ora bloccaMSG_OOBlato renderer). - Difese dell’allocator: Rafforzare la randomizzazione delle freelist SLUB o imporre page coloring per cache per complicare il riciclo deterministico delle pagine; limitare il numero di pipe buffer per pipeline riduce anche l’affidabilità della riallocazione.
- Monitoraggio: Esporre tramite telemetria allocazioni di page-table ad alto rate o uso anomalo delle pipe — questo exploit consuma grandi quantità di page table e pipe buffer.
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
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


