AF_UNIX MSG_OOB UAF & SKB-based kernel primitives

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Opsomming

  • Linux >=6.9 het ’n foutiewe manage_oob() refactor (5aa57d9f2d53) vir AF_UNIX MSG_OOB hantering bekend gestel. Gestapelde zero-length SKBs omseil die logika wat u->oob_skb skoonmaak, sodat ’n gewone recv() die out-of-band SKB kon vrymaak terwyl die pointer steeds lewend was, wat gelei het tot CVE-2025-38236.
  • Heraktiveer van recv(..., MSG_OOB) dereferensieer die dangling struct sk_buff. Met MSG_PEEK word die pad unix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user() ’n stabiele 1-byte arbitrary kernel read; sonder MSG_PEEK verhoog die primitive UNIXCB(oob_skb).consumed by offset 0x44, d.w.s. voeg +4 GiB by die boonste dword van enige 64-bit waarde wat by offset 0x40 in die heralloceerde objek geplaas is.
  • Deur order-0/1 unmovable pages leeg te dreineer (page-table spray), ’n SKB slab page geforceerd in die buddy allocator vry te maak, en die fisiese bladsy as ’n pipe buffer te hergebruik, smeed die exploit SKB metadata in beheerde geheue om die dangling bladsy te identifiseer en die read-primitive na .data, vmemmap, per-CPU, en page-table streke te draai ondanks usercopy hardening.
  • Dieselfde bladsy kan later as die boonste kernel-stack bladsy van ’n pas gekloonde draad herwin word. CONFIG_RANDOMIZE_KSTACK_OFFSET word ’n orakel: deur die stack-layout te peil terwyl pipe_write() blokkeer, wag die aanvaller totdat die uitgespoelde copy_page_from_iter() lengte (R14) by offset 0x40 land, en aktiveer dan die +4 GiB verhoging om die stack-waarde te korrupteer.
  • ’n Self-looping skb_shinfo()->frag_list hou die UAF syscall in kernel-ruimte draai totdat ’n samewerkende draad copy_from_iter() laat stagneer (via mprotect() oor ’n VMA wat ’n enkele MADV_DONTNEED gap bevat). Breek van die lus laat die verhoging vry presies wanneer die stack-doel lewendig is, wat die bytes argument opblaas sodat copy_page_from_iter() verby die pipe buffer bladsy skryf en in die volgende fisiese bladsy beland.
  • Deur pipe-buffer PFNs en page tables met die read-primitive te monitor, verseker die aanvaller dat die volgende bladsy ’n PTE-bladsy is, skakel die OOB copy om na arbitrary PTE writes, en verkry onbeperkte kernel read/write/execute. Chrome het bereikbaarheid verlaag deur MSG_OOB van renderers te blokkeer (6711812), en Linux het die logika-fout reggestel in 32ca245464e1 en CONFIG_AF_UNIX_OOB bekendgestel om die funksie opsioneel te maak.

Hoofoorzaak: manage_oob() neem net een zero-length SKB aan

unix_stream_read_generic() verwag dat elke SKB wat deur manage_oob() teruggegee word unix_skb_len() > 0 het. Na 93c99f21db36 het manage_oob() die skb == u->oob_skb cleanup-pad oorgeslaan wanneer dit eerstens ’n zero-length SKB verwyder het wat deur recv(MSG_OOB) agtergelaat is. Die daaropvolgende herstel (5aa57d9f2d53) het steeds van die eerste zero-length SKB na skb_peek_next() gevorder sonder om die lengte weer na te gaan. Met twee opeenvolgende zero-length SKBs het die funksie die tweede leë SKB teruggegee; unix_stream_read_generic() het dit toe oorgeslaan sonder om weer manage_oob() te roep, sodat die werklike OOB SKB gedekeurieëer en vrygemaak is terwyl u->oob_skb steeds daarna gewys het.

Minimale trigger-sekwensie

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 exposed by unix_stream_recv_urg()

  1. 1-byte arbitrary read (repeatable): state->recv_actor() voer uiteindelik copy_to_user(user, skb_sourced_addr, 1) uit. As die dangling SKB heralloceer word in attacker-controlled memory (of in ’n beheerde alias soos ’n pipe page), kopieer elke recv(MSG_OOB | MSG_PEEK) ’n byte van ’n arbitrêre kernel-adres wat deur __check_object_size() toegelaat word na gebruikersruimte sonder om te crash. Deur MSG_PEEK aan te laat, word die dangling pointer behouden vir onbeperkte reads.
  2. Constrained write: Wanneer MSG_PEEK uit is, verhoog UNIXCB(oob_skb).consumed += 1 die 32-bit veld by offset 0x44. Op 0x100-geïntegreerde SKB-allokasies sit dit vier bytes bo ’n 8-byte-uitgelijnde woord, wat die primitive omskakel in ’n +4 GiB increment van die woord by offset 0x40. Om dit ’n kernel write te maak, verg dit om ’n sensitiewe 64-bit waarde by daardie offset te plaas.

Reallocating the SKB page for arbitrary read

  1. Drain order-0/1 unmovable freelists: Map ’n reuse read-only anonymous VMA en fault elke bladsy om page-table allokasie te dwing (order-0 unmovable). Vul ~10% van RAM met page tables om te verseker dat opvolgende skbuff_head_cache allokasies vars buddy pages trek sodra order-0 lists uitgeput is.
  2. Spray SKBs and isolate a slab page: Gebruik dosyne stream socketpairs en queue honderde klein messages per socket (~0x100 bytes per SKB) om skbuff_head_cache te bevolk. Free gekose SKBs om ’n teiken slab page heeltemal onder attacker control te plaas en monitor sy struct page refcount via die verkrygde read primitive.
  3. Return the slab page to the buddy allocator: Free elke object op die bladsy, en voer dan genoeg addisionele allocations/frees uit om die bladsy uit SLUB per-CPU partial lists en per-CPU page lists te stoot sodat dit ’n order-1 bladsy op die buddy freelist word.
  4. Reallocate as pipe buffer: Skep honderde pipes; elke pipe reserveer minstens twee 0x1000-byte data bladsye (PIPE_MIN_DEF_BUFFERS). Wanneer die buddy allocator ’n order-1 bladsy split, hergebruik een helfte die vrygestelde SKB-bladsy. Om te lokaliseer watter pipe en watter offset oob_skb alias, skryf unike marker bytes in fake SKBs wat regdeur pipe pages gestoor is en roep herhaalde recv(MSG_OOB | MSG_PEEK) tot die marker teruggegee word.
  5. Forge a stable SKB layout: Vul die aliased pipe page met ’n fake struct sk_buff waarvan data/head pointers en skb_shared_info struktuur na arbitrêre kernel adresse van belang wys. Omdat x86_64 SMAP binne copy_to_user() uitskakel, kan user-mode adresse as staging buffers dien totdat kernel pointers bekend is.
  6. Respect usercopy hardening: Die copy slaag teen .data/.bss, vmemmap entries, per-CPU vmalloc ranges, ander threads se kernel stacks, en direct-map pages wat nie oor hoër-orde folio-grense straddle nie. Reads teen .text of gespesialiseerde caches wat deur __check_heap_object() geweier word, gee net -EFAULT terug sonder om die proses te kill.

Introspecting allocators with the read primitive

  • Break KASLR: Lees enige IDT descriptor vanaf die fixed mapping by CPU_ENTRY_AREA_RO_IDT_VADDR (0xfffffe0000000000) en trek die bekende handler offset af om die kernel base te herwin.
  • SLUB/buddy state: Globale .data symboles openbaar kmem_cache bases, terwyl vmemmap entries elke bladsy se type flags, freelist pointer, en eienaar cache blootlê. Skandeer per-CPU vmalloc segmente om struct kmem_cache_cpu instansies te vind sodat die volgende allocation adres van sleutel caches (bv. skbuff_head_cache, kmalloc-cg-192) voorspelbaar raak.
  • Page tables: In plaas daarvan om mm_struct te lees (wat deur usercopy geblokkeer word), loop die globale pgd_list (struct ptdesc) en pas die huidige mm_struct via cpu_tlbstate.loaded_mm. Sodra die root pgd bekend is, kan die primitive elke page table traverseer om PFNs vir pipe buffers, page tables, en kernel stacks te map.

Recycling the SKB page as the top kernel-stack page

  1. Free die beheerste pipe page weer en bevestig via vmemmap dat sy refcount terugkeer na nul.
  2. Allokeer onmiddellik vier helper pipe pages en free dit dan in omgekeerde volgorde sodat die buddy allocator se LIFO-gedrag deterministies is.
  3. Roep clone() om ’n helper thread te spawn; Linux stacks is vier bladsye op x86_64, so die vier mees onlangs vrygestelde bladsye word sy stack, met die laaste vrygestelde bladsy (die voormalige SKB page) by die hoogste adresse.
  4. Verifieer via page-table walk dat die helper thread se top stack PFN gelyk is aan die gerecyclede SKB PFN.
  5. Gebruik die arbitrary read om die stack layout te observeer terwyl jy die thread stuur na pipe_write(). CONFIG_RANDOMIZE_KSTACK_OFFSET trek ’n ewekansige 0x0–0x3f0 (uitgelijnd) van RSP per syscall; herhaalde writes gekombineer met poll()/read() van ’n ander thread openbaar wanneer die writer block met die gewenste offset. Wanneer geluk, sit die spilled copy_page_from_iter() bytes argument (R14) by offset 0x40 binne die gerecyclede bladsy.

Placing fake SKB metadata on the stack

  • Gebruik sendmsg() op ’n AF_UNIX datagram socket: die kernel kopieer die user sockaddr_un in ’n stack-resident sockaddr_storage (tot 108 bytes) en die ancillary data in ’n ander on-stack buffer voordat die syscall block vir queue space. Dit laat toe om ’n presiese fake SKB struktuur in stack memory te plant.
  • Detecteer wanneer die copy klaar is deur ’n 1-byte control message te verskaf wat in ’n unmapped user page geleë is; ____sys_sendmsg() fault dit in, so ’n helper thread wat mincore() op daardie adres poll, leer wanneer die bestemming-bladsy teenwoordig is.
  • Zero-initialized padding van CONFIG_INIT_STACK_ALL_ZERO vul handig die ongebruikte velde, wat ’n geldige SKB header voltooi sonder ekstra skryfwerk.

Timing the +4 GiB increment with a self-looping frag list

  • Forge skb_shinfo(fakeskb)->frag_list om na ’n tweede fake SKB te wys (gestoor in attacker-controlled user memory) wat len = 0 en next = &self het. Wanneer skb_walk_frags() hierdie lys iter binne __skb_datagram_iter(), spin uitvoering oneindig want die iterator bereik nooit NULL nie en die copy-lus maak geen vordering nie.
  • Hou die recv syscall aan die gang binne die kernel deur die tweede fake SKB self-loop te laat. Wanneer dit tyd is om die increment te vuur, verander eenvoudig die tweede SKB se next pointer vanaf user space na NULL. Die lus eindig en unix_stream_recv_urg() voer onmiddellik UNIXCB(oob_skb).consumed += 1 uit een keer, wat enige object wat tans die gerecyclede stack page by offset 0x40 huisves, beïnvloed.

Stalling copy_from_iter() without userfaultfd

  • Map ’n reuse anonymous RW VMA en fault dit volledig in.
  • Punch ’n single-page gap met madvise(MADV_DONTNEED, hole, PAGE_SIZE) en plaas daardie adres binne die iov_iter wat vir write(pipefd, user_buf, 0x3000) gebruik word.
  • Parallel, roep mprotect() op die hele VMA vanaf ’n ander thread. Die syscall gryp die mmap write lock en loop elke PTE. Wanneer die pipe writer die gat bereik, blokkeer die page fault handler op die mmap lock wat deur mprotect() gehou word, en pauseer copy_from_iter() op ’n deterministiese punt terwyl die spilled bytes waarde op die stack segment wat deur die gerecyclede SKB page gehost word, lê.

Turning the increment into arbitrary PTE writes

  1. Fire the increment: Vrylê die frag loop terwyl copy_from_iter() gestal is sodat die +4 GiB increment die bytes variabele tref.
  2. Overflow the copy: Sodra die fault hervat, glo copy_page_from_iter() dat dit >4 GiB kan kopieer in die huidige pipe page. Na die vulling van die legitime 0x2000 bytes (twee pipe buffers), voer dit nog ’n iterasie uit en skryf die oorblywende user data in watter fisiese bladsy ook al die pipe buffer PFN volg.
  3. Arrange adjacency: Gebruik allocator telemetry om die buddy allocator te dwing om ’n process-owned PTE page onmiddellik na die teiken pipe buffer page te plaas (bv. wissel tussen allocating pipe pages en aanraking van nuwe virtual ranges om page-table allokasie te trigger totdat die PFNs binne dieselfde 2 MiB pageblock uitlijn).
  4. Overwrite page tables: Kodeer gewenste PTE entries in die ekstra 0x1000 bytes van user data sodat die OOB copy_from_iter() die aangrensende bladsy met attacker-gekose entries vul, wat RW/RWX user mappings van kernel fisiese geheue gee of bestaande entries oorskryf om SMEP/SMAP uit te skakel.

Mitigations / hardening ideas

  • Kernel: Pas 32ca245464e1479bfea8592b9db227fdc1641705 toe (properly revalidates SKBs) en oorweeg om AF_UNIX OOB heeltemal te deaktiveer tensy dit streng nodig is via CONFIG_AF_UNIX_OOB (5155cbcdbf03). Verhard manage_oob() met addisionele sanity checks (bv. lus totdat unix_skb_len() > 0) en oudit ander socket protocols vir soortgelyke aannames.
  • Sandboxing: Filter MSG_OOB/MSG_PEEK flags in seccomp profiles of hoërvlak broker APIs (Chrome change 6711812 blokkeer nou renderer-side MSG_OOB).
  • Allocator defenses: Versterking van SLUB freelist randomization of afdwing van per-cache page coloring sou deterministiese page recycling bemoeilik; pipeline-limiting van pipe buffer tellings verminder ook reallocation betroubaarheid.
  • Monitoring: Blootlê high-rate page-table allokasie of abnormale pipe gebruik via telemetry—hierdie exploit verbruik groot hoeveelhede page tables en pipe buffers.

References

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks