AF_UNIX MSG_OOB UAF & SKB-आधारित kernel primitives
Tip
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
TL;DR
- Linux >=6.9 introduced a flawed
manage_oob()refactor (5aa57d9f2d53) for AF_UNIXMSG_OOBhandling. Stacked zero-length SKBs ने उस लॉजिक को बायपास कर दिया जोu->oob_skbको क्लियर करता है, इसलिए एक सामान्यrecv()out-of-band SKB को free कर सकता था जबकि pointer लाइव रहा, जिससे CVE-2025-38236 हुआ। - Re-triggering
recv(..., MSG_OOB)danglingstruct sk_buffका dereference कर देता है।MSG_PEEKके साथ, pathunix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()एक स्थिर 1-बाइट arbitrary kernel read बन जाती है; बिनाMSG_PEEKके primitiveUNIXCB(oob_skb).consumedको offset0x44पर increment करता है, यानी reallocated object के offset0x40में रखे किसी भी 64-bit value के upper dword में +4 GiB जोड़ देता है। - order-0/1 unmovable pages को drain करके (page-table spray), एक SKB slab page को buddy allocator में force-free करके, और उसी physical page को pipe buffer के रूप में reuse करके, exploit controlled memory में SKB metadata बनाता है ताकि dangling page पहचान सके और read primitive को
.data, vmemmap, per-CPU, और page-table regions में pivot करे, usercopy hardening के बावजूद। - वही page बाद में ताज़ा cloned thread के top kernel-stack page के रूप में recycle किया जा सकता है।
CONFIG_RANDOMIZE_KSTACK_OFFSETएक oracle बन जाता है:pipe_write()ब्लॉक होने के दौरान stack layout को probe करके, attacker तब तक इंतज़ार करता है जब तक spilledcopy_page_from_iter()length (R14) offset0x40पर नहीं आता, फिर +4 GiB increment फायर करके stack value को corrupt कर देता है। - self-looping
skb_shinfo()->frag_listUAF syscall को kernel space में घुमाता रखता है जब तक एक सहयोगी threadcopy_from_iter()को stall न कर दे (एक ऐसे VMA परmprotect()के माध्यम से जिसमें singleMADV_DONTNEEDhole हो)। लूप तोड़ने पर increment उसी समय रिलीज़ होता है जब stack target live होता है,bytesargument को inflate कर देता है ताकिcopy_page_from_iter()pipe buffer page के पार अगले physical page में लिख दे। - read primitive से pipe-buffer PFNs और page tables की मॉनिटरिंग करके, attacker यह सुनिश्चित करता है कि अगला page एक PTE page है, OOB copy को arbitrary PTE writes में बदल देता है, और unrestricted kernel read/write/execute प्राप्त कर लेता है। Chrome ने renderers से
MSG_OOBब्लॉक करके पहुंच को कम किया (6711812), और Linux ने logic flaw को32ca245464e1में फिक्स किया तथा feature को optional बनाने के लिएCONFIG_AF_UNIX_OOBजोड़ा।
Root cause: manage_oob() assumes only one zero-length SKB
unix_stream_read_generic() यह मानता है कि manage_oob() द्वारा लौटाया गया हर SKB का unix_skb_len() > 0 होगा। 93c99f21db36 के बाद, manage_oob() ने उस skb == u->oob_skb cleanup path को स्किप कर दिया जब भी उसने पहला recv(MSG_OOB) द्वारा छोड़ा गया zero-length SKB हटाया। बाद के फिक्स (5aa57d9f2d53) ने पहले zero-length SKB से skb_peek_next() की ओर आगे बढ़ना जारी रखा बिना लंबाई को फिर से जाँचे। दो लगातार zero-length SKBs के साथ, function दूसरे empty SKB को लौटाती थी; unix_stream_read_generic() ने उसे फिर से कॉल किए बिना स्किप कर दिया, इसलिए असली OOB SKB dequeue और free हो गया जबकि u->oob_skb अभी भी उस पर पॉइंट कर रहा था।
Minimal trigger sequence
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
unix_stream_recv_urg() द्वारा एक्सपोज़ प्रिमिटिव्स
- 1-byte arbitrary read (repeatable):
state->recv_actor()अंततःcopy_to_user(user, skb_sourced_addr, 1)करता है। यदि dangling SKB attacker-controlled memory में पुन:आवंटित हो जाता है (या किसी controlled alias जैसे pipe page में), तो हरrecv(MSG_OOB | MSG_PEEK)__check_object_size()द्वारा अनुमति प्राप्त किसी भी arbitrary kernel एड्रेस से एक बाइट user space में बिना क्रैश किए कॉपी कर देता है।MSG_PEEKचालू रखने से dangling pointer अपरिमित पढ़नों के लिए बरकरार रहता है। - Constrained write: जब
MSG_PEEKclear होता है, तोUNIXCB(oob_skb).consumed += 1offset0x44पर मौजूद 32-bit फ़ील्ड को इन्क्रीमेंट करता है। 0x100-एलाइंड SKB allocations पर यह 8-बाइट एलाइन वाले शब्द से चार बाइट ऊपर बैठता है, जिससे प्रिमिटिव उस ऑफ़सेट0x40पर होस्ट किए गए शब्द में +4 GiB इन्क्रीमेंट में बदल जाता है। इसे kernel write में बदलने के लिए उस ऑफ़सेट पर कोई संवेदनशील 64-bit वैल्यू पॉज़िशन करनी होगी।
Arbitrary read के लिए SKB page को फिर से आवंटित करना
- Drain order-0/1 unmovable freelists: एक बड़ा read-only anonymous VMA मैप करें और पेज-टेबल अलोकेशन मजबूर करने के लिए हर पेज को fault कराएँ (order-0 unmovable)। लगभग RAM का ~10% page tables से भरने पर subsequent
skbuff_head_cacheallocations fresh buddy pages खींचते हैं जब order-0 lists खत्म हो जाती हैं। - Spray SKBs और एक slab page अलग करें: दर्जनों stream socketpairs का उपयोग करें और हर socket पर सैंकड़ों छोटे संदेश queue करें (~0x100 bytes प्रति SKB) ताकि
skbuff_head_cacheभरा जाए। चुने हुए SKBs को free करके target slab page पूरी तरह attacker control में ले आएँ और उसकेstruct pagerefcount को emerging read primitive से मॉनिटर करें। - Slab page को buddy allocator को लौटाएँ: पेज पर मौजूद हर object को free करें, फिर अतिरिक्त allocations/frees करके page को SLUB per-CPU partial lists और per-CPU page lists से बाहर धकेलें ताकि वह buddy freelist पर order-1 पेज बन जाए।
- Pipe buffer के रूप में पुन:आवंटन: सैकड़ों pipes बनाएं; हर pipe कम से कम दो 0x1000-बाइट data पेज रिज़र्व करता है (
PIPE_MIN_DEF_BUFFERS)। जब buddy allocator एक order-1 पेज स्प्लिट करता है, तो एक आधा freed SKB पेज को reuse करता है। यह पता लगाने के लिए कि कौन सा pipe और किस offset नेoob_skbको alias किया है, fake SKBs में यूनिक marker bytes लिखें जो pipe पेजों में फैले हों और तब तक बार-बारrecv(MSG_OOB | MSG_PEEK)कॉल करें जब तक marker वापस न आ जाए। - Stable SKB layout तैयार करें: aliased pipe page को एक fake
struct sk_buffसे भरें जिनकेdata/headpointers औरskb_shared_infoस्ट्रक्चर arbitrary kernel एड्रेसों की ओर इशारा करते हों। चूंकि x86_64 परcopy_to_user()के अंदर SMAP डिसेबल रहता है, user-mode एड्रेस staging buffers के रूप में तब तक उपयोग हो सकते हैं जब तक kernel pointers ज्ञात न हों। - usercopy हार्डनिंग का ध्यान रखें: कॉपी
.data/.bss, vmemmap एंट्रियों, per-CPU vmalloc रेंजेज़, अन्य थ्रेड्स के kernel stacks, और direct-map पेजेज़ के खिलाफ काम करती है जो उच्च-ऑर्डर folio सीमाओं को पार नहीं करते।.textया specialized caches के खिलाफ पढ़ने पर__check_heap_object()अस्वीकृति पर बस-EFAULTलौटता है बिना प्रोसेस को मारने के।
Read primitive से allocators का निरीक्षण
- KASLR तोड़ना: fixed mapping पर
CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000) से कोई भी IDT descriptor पढ़ें और ज्ञात handler offset घटाकर kernel base पुनर्प्राप्त करें। - SLUB/buddy स्थिति: Global
.datasymbolskmem_cacheबेस दिखाते हैं, जबकि vmemmap एंट्रीज़ हर पेज के type flags, freelist pointer, और owning cache उजागर करती हैं। per-CPU vmalloc segments स्कैन करने सेstruct kmem_cache_cpuइंस्टेंस मिलते हैं ताकि key caches (जैसेskbuff_head_cache,kmalloc-cg-192) के अगले allocation addresses अनुमानित हो जाएँ। - Page tables:
mm_structपढ़ने के बजाय (usercopy द्वारा ब्लॉक), globalpgd_list(struct ptdesc) को वॉक करके औरcpu_tlbstate.loaded_mmके द्वारा वर्तमानmm_structसे मैचे करके rootpgdज्ञात करें। एक बार rootpgdज्ञात होने पर primitive हर पेज टेबल को ट्रैवर्स कर सकती है ताकि pipe buffers, page tables, और kernel stacks के PFNs मैप किए जा सकें।
SKB पेज को top kernel-stack पेज के रूप में रिसायकल करना
- Controlled pipe page को फिर से free करें और vmemmap से पुष्टि करें कि उसका refcount फिर से शून्य हो गया है।
- तुरंत चार helper pipe पेज allocate करें और फिर उन्हें reverse order में free करें ताकि buddy allocator का LIFO व्यवहार deterministic बने।
clone()कॉल करके एक helper थ्रेड स्पॉन करें; x86_64 पर Linux stacks चार पेज होते हैं, इसलिए चार सबसे हाल में freed पेज उसका stack बन जाते हैं, जिसमें आखिरी freed पेज (पूर्व SKB पेज) उच्चतम एड्रेसेज़ पर होता है।- Page-table walk द्वारा सत्यापित करें कि helper थ्रेड का top stack PFN recycled SKB PFN के समान है।
- Arbitrary read का उपयोग करके stack layout देखें और थ्रेड को
pipe_write()में निर्देशित करें।CONFIG_RANDOMIZE_KSTACK_OFFSETप्रत्येक syscall परRSPसे एक रैंडम 0x0–0x3f0 (aligned) घटाता है; repeated writes और दूसरे थ्रेड सेpoll()/read()मिलाकर पता चलता है जब writer वांछित offset के साथ block हो। किस्मत से, spilledcopy_page_from_iter()काbytesargument (R14) recycled page के अंदर offset0x40पर आ सकता है।
Stack पर fake SKB metadata रखने का तरीका
- AF_UNIX datagram socket पर
sendmsg()का उपयोग करें: kernel usersockaddr_unको stack-residentsockaddr_storage(अधिकतम 108 bytes) में और ancillary data को syscall के ब्लॉक होने से पहले एक अन्य on-stack buffer में कॉपी करता है। इससे stack मेमोरी में सटीक fake SKB संरचना प्लांट करना संभव होता है। - तब पता लगाएँ कि copy कब खत्म हुई है: एक 1-बाइट control message दें जो कि unmapped user page में स्थित हो;
____sys_sendmsg()इसे fault करके लाएगा, तो उस address परmincore()पोल कर रहा helper थ्रेड यह जान लेता है कि destination page कब मौजूद है। CONFIG_INIT_STACK_ALL_ZEROसे मिलने वाला zero-initialized padding अनावश्यक फ़ील्ड भर देता है, जिससे अतिरिक्त लेखन के बिना एक वैध SKB हेडर बन जाता है।
self-looping frag list के साथ +4 GiB इन्क्रीमेंट का टाइमिंग
skb_shinfo(fakeskb)->frag_listको दूसरे fake SKB (attacker-controlled user memory में रखा हुआ) की ओर पॉइंट करने के लिए बनाएं, जिसकीlen = 0औरnext = &selfहो। जबskb_walk_frags()यह लिस्ट__skb_datagram_iter()के अंदर iterate करती है, तो iterator कभीNULLतक नहीं पहुँचता और copy loop बिना प्रगति के अनंतकाल तक घूमता रहता है।- recv syscall को kernel के अंदर चलते रहने दें ताकि दूसरा fake SKB self-loop करे। जब इन्क्रीमेंट फ़ायर करना हो, तो बस दूसरे SKB का
nextpointer user space सेNULLमें बदल दें। लूप निकल जाता है औरunix_stream_recv_urg()तुरंतUNIXCB(oob_skb).consumed += 1को execute करता है, जो उस ऑब्जेक्ट को प्रभावित करेगा जो वर्तमान में recycled stack page के offset0x40पर है।
userfaultfd के बिना copy_from_iter() को स्टाल करना
- एक विशाल anonymous RW VMA मैप करें और उसे पूरी तरह fault कराएँ।
madvise(MADV_DONTNEED, hole, PAGE_SIZE)से एक single-page hole बनाएं और उस address कोiov_iterमें रखें जोwrite(pipefd, user_buf, 0x3000)के लिए उपयोग हो रहा है।- समानांतर में, दूसरे थ्रेड से पूरे VMA पर
mprotect()कॉल करें। syscall mmap write lock लेता है और हर PTE को वॉक करता है। जब pipe writer hole पर पहुँचता है, तो page fault handler mmap lock पर block हो जाता है जोmprotect()द्वारा होल्ड किया गया है, और इस तरहcopy_from_iter()एक deterministic पॉइंट पर रुक जाता है जबकि spilledbytesवैल्यू recycled SKB पेज पर होस्ट किए गए stack segment पर रहती है।
इन्क्रीमेंट को arbitrary PTE writes में बदलना
- इन्क्रीमेंट फ़ायर करें: frag loop को तब रिलीज़ करें जब
copy_from_iter()स्टाल हो ताकि +4 GiB इन्क्रीमेंटbytesवैरिएबल पर लगे। - copy को overflow करें: जब fault resume होता है,
copy_page_from_iter()मानता है कि वह >4 GiB कॉपी कर सकता है current pipe page में। वैध 0x2000 bytes (दो pipe buffers) भरने के बाद यह एक और iteration चलाता है और शेष user data को उस physical पेज में लिख देता है जो pipe buffer PFN के बाद आता है। - adjacency सेट करें: allocator telemetry का उपयोग करके buddy allocator को मजबूर करें कि वह target pipe buffer पेज के ठीक बाद एक process-owned PTE पेज रखे (उदाहरण के लिए, pipe पेजों को alternate करें और नई virtual रेंजेज़ को छुएँ ताकि page-table allocation ट्रिगर हो जब तक PFNs एक ही 2 MiB pageblock में संरेखित न हो जाएँ)।
- page tables overwrite करें: अतिरिक्त 0x1000 bytes user data में इच्छित PTE एंट्रीज़ एन्कोड करें ताकि OOB
copy_from_iter()पड़ोसी पेज को attacker-चुने हुए एंट्रीज़ से भर दे, जिससे kernel physical memory के RW/RWX user mappings मिलें या मौजूदा एंट्रीज़ rewrite होकर SMEP/SMAP डिसेबल कर दी जाएँ।
Mitigations / hardening ideas
- Kernel:
32ca245464e1479bfea8592b9db227fdc1641705लागू करें (properly revalidates SKBs) और AF_UNIX OOB को तब तक disable करने पर विचार करें जब तक वह सख्ती से आवश्यक न हो,CONFIG_AF_UNIX_OOB(5155cbcdbf03) के माध्यम से।manage_oob()को अतिरिक्त sanity checks (उदा., loop untilunix_skb_len() > 0) के साथ हार्डन करें और समान अनुमानों के लिए अन्य socket protocols का ऑडिट करें। - Sandboxing: seccomp प्रोफाइल्स या higher-level broker APIs में
MSG_OOB/MSG_PEEKफ्लैग्स को फ़िल्टर करें (Chrome change6711812अब renderer-sideMSG_OOBको ब्लॉक करता है)। - Allocator defenses: SLUB freelist randomization को मजबूत करना या per-cache page coloring लागू करना deterministic page recycling को जटिल बनाएगा; pipe buffer counts का pipeline-limiting भी reallocation reliability घटाता है।
- Monitoring: high-rate page-table allocation या असामान्य pipe उपयोग को telemetry के माध्यम से उजागर करें—यह exploit बड़े पैमाने पर page tables और 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
AWS हैकिंग सीखें और अभ्यास करें:
HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें:HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।


