AF_UNIX MSG_OOB UAF & SKB-based kernel primitives
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
TL;DR
- Linux >=6.9 a introduit un refactor défectueux
manage_oob()(5aa57d9f2d53) pour la gestion de AF_UNIXMSG_OOB. Des SKB empilés de longueur zéro contournent la logique qui effaceu->oob_skb, donc unrecv()normal pouvait free l’SKB out-of-band tandis que le pointeur restait vivant, menant à CVE-2025-38236. - Relancer
recv(..., MSG_OOB)déréférence lestruct sk_buffpendouillant. AvecMSG_PEEK, le cheminunix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()devient une lecture arbitraire stable d’1 octet dans le kernel ; sansMSG_PEEKle primitive incrémenteUNIXCB(oob_skb).consumedà l’offset0x44, i.e. ajoute +4 GiB au dword supérieur de toute valeur 64-bit placée à l’offset0x40dans l’objet réalloué. - En drainant des pages non déplaçables d’ordre 0/1 (page-table spray), en forçant la libération d’une page de slab SKB dans l’allocator buddy, et en réutilisant la page physique comme pipe buffer, l’exploit forge des métadonnées SKB dans une mémoire contrôlée pour identifier la page pendouillante et pivoter le primitive de lecture vers
.data, vmemmap, per-CPU et les régions de page-table malgré le usercopy hardening. - La même page peut ensuite être recyclée comme page supérieure de kernel-stack d’un thread nouvellement cloné.
CONFIG_RANDOMIZE_KSTACK_OFFSETdevient un oracle : en sondant la disposition de la pile pendant quepipe_write()bloque, l’attaquant attend que la longueur renversée decopy_page_from_iter()(R14) tombe à l’offset0x40, puis déclenche l’incrément de +4 GiB pour corrompre la valeur sur la pile. - Une
skb_shinfo()->frag_listauto-bouclante maintient l’appel UAF en boucle dans l’espace kernel jusqu’à ce qu’un thread coopérant bloquecopy_from_iter()(viamprotect()sur un VMA contenant un trouMADV_DONTNEED). Casser la boucle libère l’incrément exactement quand la cible sur la pile est vivante, gonflant l’argumentbytesde sorte quecopy_page_from_iter()écrit au-delà de la page de pipe buffer vers la page physique suivante. - En surveillant les PFNs des pipe-buffers et les tables de pages avec le primitive de lecture, l’attaquant s’assure que la page suivante est une page PTE, convertit la copie OOB en écritures PTE arbitraires, et obtient lecture/écriture/exécution kernel sans restriction. Chrome a atténué l’accès en bloquant
MSG_OOBdepuis les renderers (6711812), et Linux a corrigé la faille logique dans32ca245464e1et introduitCONFIG_AF_UNIX_OOBpour rendre la fonctionnalité optionnelle.
Root cause: manage_oob() assumes only one zero-length SKB
unix_stream_read_generic() s’attend à ce que chaque SKB retourné par manage_oob() ait unix_skb_len() > 0. Après 93c99f21db36, manage_oob() a sauté le chemin de nettoyage skb == u->oob_skb chaque fois qu’il supprimait en premier un SKB de longueur zéro laissé par recv(MSG_OOB). La correction ultérieure (5aa57d9f2d53) avançait encore depuis le premier SKB de longueur zéro vers skb_peek_next() sans re-vérifier la longueur. Avec deux SKB consécutifs de longueur zéro, la fonction retournait le second SKB vide ; unix_stream_read_generic() l’ignorait ensuite sans appeler à nouveau manage_oob(), de sorte que le vrai SKB OOB était désenfilé et free alors que u->oob_skb pointait encore dessus.
Séquence minimale de déclenchement
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
Primitives exposées par unix_stream_recv_urg()
- 1-byte arbitrary read (repeatable):
state->recv_actor()effectue finalementcopy_to_user(user, skb_sourced_addr, 1). Si le SKB dangling est réalloué dans une zone contrôlée par l’attaquant (ou dans un alias contrôlé comme une pipe page), chaquerecv(MSG_OOB | MSG_PEEK)copie un octet depuis une adresse kernel arbitraire autorisée par__check_object_size()vers l’espace utilisateur sans provoquer de plantage. GarderMSG_PEEKactivé préserve le pointeur suspendu pour des lectures illimitées. - Constrained write : Quand
MSG_PEEKest désactivé,UNIXCB(oob_skb).consumed += 1incrémente le champ 32 bits à l’offset0x44. Sur des allocations SKB alignées sur0x100ceci se situe quatre octets au-dessus d’un mot aligné sur 8 octets, convertissant la primitive en un incrément de +4 GiB du mot hébergé à l’offset0x40. Transformer cela en écriture kernel nécessite de positionner une valeur 64 bits sensible à cet offset.
Reallocating the SKB page for arbitrary read
- Drain order-0/1 unmovable freelists : Mappez une énorme VMA anonyme en lecture seule et faultez chaque page pour forcer l’allocation des tables de pages (order-0 unmovable). Remplir ~10% de la RAM avec des tables de pages garantit que les allocations suivantes de
skbuff_head_cacherécupéreront des pages buddy fraîches une fois les listes order-0 épuisées. - Spray SKBs and isolate a slab page : Utilisez des dizaines de stream socketpairs et enfilez des centaines de petits messages par socket (~0x100 octets par SKB) pour remplir
skbuff_head_cache. Freez des SKB choisis pour pousser une page de slab cible entièrement sous contrôle de l’attaquant et surveillez son refcountstruct pagevia la primitive de lecture émergente. - Return the slab page to the buddy allocator : Freez chaque objet sur la page, puis effectuez suffisamment d’allocations/frees supplémentaires pour expulser la page des listes partielles per-CPU de SLUB et des listes per-CPU de pages afin qu’elle devienne une page order-1 dans le buddy freelist.
- Reallocate as pipe buffer : Créez des centaines de pipes ; chaque pipe réserve au moins deux pages de données de
0x1000octets (PIPE_MIN_DEF_BUFFERS). Quand le buddy allocator scinde une page order-1, une moitié réutilise la page SKB libérée. Pour localiser quel pipe et quel offset aliasentoob_skb, écrivez des octets marqueurs uniques dans de faux SKB stockés à travers les pages de pipe et appelez répétitivementrecv(MSG_OOB | MSG_PEEK)jusqu’à ce que le marqueur soit retourné. - Forge a stable SKB layout : Remplissez la page de pipe aliasée avec un faux
struct sk_buffdont les pointeursdata/headet la structureskb_shared_infopointent vers des adresses kernel arbitraires d’intérêt. Parce que x86_64 désactive SMAP à l’intérieur decopy_to_user(), des adresses en mode utilisateur peuvent servir de buffers de staging tant que les pointeurs kernel ne sont pas connus. - Respect usercopy hardening : La copie réussit contre
.data/.bss, les entrées vmemmap, les plages vmalloc per-CPU, les stacks kernel d’autres threads et les pages direct-map qui ne chevauchent pas des folios d’ordre supérieur. Les lectures contre.textou des caches spécialisés rejetés par__check_heap_object()retournent simplement-EFAULTsans tuer le processus.
Introspecting allocators with the read primitive
- Break KASLR : Lisez n’importe quel descripteur IDT depuis le mapping fixe à
CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000) et soustrayez l’offset connu du handler pour récupérer la base du kernel. - SLUB/buddy state : Les symboles globaux
.datarévèlent les bases dekmem_cache, tandis que les entrées vmemmap exposent les flags de type de chaque page, le pointeur de freelist et le cache propriétaire. Scanner les segments vmalloc per-CPU découvre des instancesstruct kmem_cache_cpude sorte que l’adresse d’allocation suivante de caches clés (p.ex.skbuff_head_cache,kmalloc-cg-192) devienne prédictible. - Page tables : Plutôt que de lire
mm_struct(bloqué par usercopy), parcourez lapgd_listglobale (struct ptdesc) et faites correspondre lemm_structcourant viacpu_tlbstate.loaded_mm. Une fois lepgdracine connu, la primitive peut traverser toutes les tables de pages pour mapper les PFN des pipe buffers, des tables de pages et des stacks kernel.
Recycling the SKB page as the top kernel-stack page
- Freez à nouveau la page de pipe contrôlée et confirmez via vmemmap que son refcount revient à zéro.
- Allouez immédiatement quatre pages de pipe aide et freez-les ensuite dans l’ordre inverse afin que le comportement LIFO du buddy allocator soit déterministe.
- Appelez
clone()pour lancer un thread helper ; les stacks Linux font quatre pages sur x86_64, donc les quatre pages les plus récemment freed deviennent sa stack, la dernière page freed (l’ancienne page SKB) étant aux adresses les plus hautes. - Vérifiez via un parcours des tables de pages que le PFN de la top stack du thread helper est égal au PFN SKB recyclé.
- Utilisez la lecture arbitraire pour observer le layout de la stack tout en orientant le thread vers
pipe_write().CONFIG_RANDOMIZE_KSTACK_OFFSETsoustrait un offset aléatoire0x0–0x3f0(aligné) deRSPpar syscall ; des écritures répétées combinées àpoll()/read()depuis un autre thread révèlent quand l’écrivain se bloque avec l’offset désiré. Avec de la chance, l’argumentbytesrenvoyé parcopy_page_from_iter()(R14) se trouve à l’offset0x40à l’intérieur de la page recyclée.
Placing fake SKB metadata on the stack
- Utilisez
sendmsg()sur un socket AF_UNIX datagram : le kernel copie lesockaddr_unutilisateur dans unsockaddr_storagerésidant sur la stack (jusqu’à 108 octets) et les données accessoires dans un autre buffer sur la stack avant que le syscall ne bloque en attendant de l’espace dans la queue. Cela permet d’implanter une structure fake SKB précise dans la mémoire de la stack. - Détectez la fin de la copie en fournissant un message de contrôle d’1 octet situé dans une page utilisateur non mappée ;
____sys_sendmsg()la fault, ainsi un thread aide faisant dupollsurmincore()à cette adresse apprend quand la page destination est présente. - Le padding initialisé à zéro par
CONFIG_INIT_STACK_ALL_ZEROremplit commodément les champs inutilisés, complétant un header SKB valide sans écritures supplémentaires.
Timing the +4 GiB increment with a self-looping frag list
- Forgez
skb_shinfo(fakeskb)->frag_listpour pointer vers un second fake SKB (stocké en mémoire utilisateur contrôlée) qui alen = 0etnext = &self. Quandskb_walk_frags()itère cette liste à l’intérieur de__skb_datagram_iter(), l’exécution tourne indéfiniment car l’itérateur n’atteint jamaisNULLet la boucle de copie ne progresse pas. - Gardez l’appel recv en cours dans le kernel en laissant le second fake SKB en auto-boucle. Quand il est temps de déclencher l’incrément, changez simplement le pointeur
nextdu second SKB depuis l’espace utilisateur versNULL. La boucle se termine etunix_stream_recv_urg()exécute immédiatementUNIXCB(oob_skb).consumed += 1une fois, affectant l’objet qui occupe actuellement la page de stack recyclée à l’offset0x40.
Stalling copy_from_iter() without userfaultfd
- Mappez une gigantesque VMA anonyme RW et faultez-la entièrement.
- Creusez un trou d’une page avec
madvise(MADV_DONTNEED, hole, PAGE_SIZE)et placez cette adresse dans l’iov_iterutilisé pourwrite(pipefd, user_buf, 0x3000). - En parallèle, appelez
mprotect()sur toute la VMA depuis un autre thread. Le syscall prend le write lock du mmap et parcourt chaque PTE. Quand le writer de pipe atteint le trou, le handler de page fault bloque sur le mmap lock détenu parmprotect(), mettant en pausecopy_from_iter()à un point déterministe pendant que la valeurbytesépanchée réside sur le segment de stack hébergé par la page SKB recyclée.
Turning the increment into arbitrary PTE writes
- Fire the increment : Libérez la boucle de frags pendant que
copy_from_iter()est en pause pour que l’incrément +4 GiB atteigne la variablebytes. - Overflow the copy : Une fois le fault repris,
copy_page_from_iter()croit pouvoir copier >4 GiB dans la page de pipe courante. Après avoir rempli les0x2000octets légitimes (deux pipe buffers), il exécute une autre itération et écrit les données utilisateur restantes dans la page physique qui suit le PFN du pipe buffer. - Arrange adjacency : En utilisant la télémétrie de l’allocator, forcez le buddy allocator à placer une page PTE appartenant au processus immédiatement après la page de pipe cible (p.ex. alternez entre allouer des pages de pipe et toucher de nouvelles plages virtuelles pour déclencher l’allocation de tables de pages jusqu’à ce que les PFN s’alignent à l’intérieur du même pageblock de 2 MiB).
- Overwrite page tables : Encodez des entrées PTE désirées dans les 0x1000 octets supplémentaires de données utilisateur de sorte que le
copy_from_iter()OOB remplisse la page voisine avec des entrées choisies par l’attaquant, accordant des mappings user RW/RWX de la mémoire physique kernel ou réécrivant des entrées existantes pour désactiver SMEP/SMAP.
Mitigations / hardening ideas
- Kernel : Appliquez
32ca245464e1479bfea8592b9db227fdc1641705(revalide correctement les SKB) et envisagez de désactiver AF_UNIX OOB entièrement sauf si strictement nécessaire viaCONFIG_AF_UNIX_OOB(5155cbcdbf03). Renforcezmanage_oob()avec des vérifications de sanity supplémentaires (p.ex. bouclez jusqu’àunix_skb_len() > 0) et auditez d’autres protocoles socket pour des hypothèses similaires. - Sandboxing : Filtrez les flags
MSG_OOB/MSG_PEEKdans les profils seccomp ou les APIs broker de plus haut niveau (le changement Chrome6711812bloque désormaisMSG_OOBcôté renderer). - Allocator defenses : Renforcer la randomisation des freelists SLUB ou appliquer un coloring par page par cache compliquerait le recyclage déterministe des pages ; limiter le nombre de buffers de pipe réduit aussi la fiabilité de la réallocation.
- Monitoring : Exposez l’allocation élevée de tables de pages ou l’utilisation anormale de pipes via la télémétrie — cet exploit consomme de grandes quantités de tables de pages et de pipe buffers.
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
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks

