AF_UNIX MSG_OOB UAF & SKB-based kernel primitives
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
TL;DR
- Linux >=6.9, AF_UNIX
MSG_OOBişleme için yapılan hatalımanage_oob()refactor’ı (5aa57d9f2d53) getirdi. Üst üste binmiş sıfır-uzunluklu SKB’ler,u->oob_skb’yi temizleyen mantığı atlatıyordu; böylece normal birrecv()out-of-band SKB’yi serbest bırakırken işaretçi canlı kalıyor ve bu durum CVE-2025-38236’a yol açıyordu. recv(..., MSG_OOB)yeniden tetiklendiğinde sarkık (dangling)struct sk_buffdereference ediliyordu.MSG_PEEKile yolunix_stream_recv_urg() -> __skb_datagram_iter() -> copy_to_user()kalıcı 1-baytlık rastgele kernel okumasına dönüşüyordu;MSG_PEEKolmadan primitive, yeniden tahsis edilen nesnenin offset0x40içindeki herhangi bir 64-bit değerin üst dword’una offset0x44’te +4 GiB ekleyenUNIXCB(oob_skb).consumeddeğerini arttırıyordu.- order-0/1 unmovable sayfaları boşaltarak (page-table spray), bir SKB slab sayfasını buddy allocator içine force-free ederek ve fiziksel sayfayı pipe buffer olarak yeniden kullanarak, exploit kontrollü bellekte SKB metadata’sı sahteleyip sarkık sayfayı tespit ediyor ve read primitive’i
.data, vmemmap, per-CPU ve page-table bölgelerine pivotluyor; bu, usercopy hardening’e rağmen mümkün oluyor. - Aynı sayfa daha sonra yeni clone’lanmış bir thread’in en üst kernel-stack sayfası olarak geri dönüştürülebiliyor.
CONFIG_RANDOMIZE_KSTACK_OFFSETbir oracle haline geliyor: attacker,pipe_write()bloklanmışken stack düzenini probe ederek, spilledcopy_page_from_iter()uzunluğu (R14) offset0x40’a denk gelene kadar bekliyor, sonra +4 GiB arttırmayı tetikleyip stack değerini bozuyor. - Kendini döngüye alan
skb_shinfo()->frag_list, UAF syscall’un kernel alanında dönmesini sağlıyor; bu süre boyunca işbirlikçi bir threadcopy_from_iter()’i (tek birMADV_DONTNEEDboşluğu içeren bir VMA üzerindemprotect()ile) duraklatıyor. Döngüyü kırmak, arttırmayı tam olarak stack hedefi canlı olduğunda serbest bırakıyor; böylecebytesargümanı şişiyor vecopy_page_from_iter()pipe buffer sayfasını aşıp sonraki fiziksel sayfaya yazıyor. - Read primitive ile pipe-buffer PFN’lerini ve page table’ları izleyerek, attacker sonraki sayfanın bir PTE sayfası olduğundan emin oluyor, OOB copy’yi arbitrary PTE write’larına çeviriyor ve sınırsız kernel read/write/execute elde ediyor. Chrome, renderer’lardan
MSG_OOB’u engelleyerek erişimi kısıtladı (6711812), Linux ise hatalı mantığı32ca245464e1ile düzeltti ve özelliği isteğe bağlı yapmak içinCONFIG_AF_UNIX_OOB’u ekledi.
Root cause: manage_oob() assumes only one zero-length SKB
unix_stream_read_generic() her manage_oob() tarafından döndürülen SKB’nin unix_skb_len() > 0 olmasını bekliyor. 93c99f21db36 sonrası, manage_oob() ilk olarak recv(MSG_OOB) tarafından geride bırakılmış sıfır-uzunluklu bir SKB’yi kaldırdığında skb == u->oob_skb temizleme yolunu atlıyordu. Sonraki düzeltme (5aa57d9f2d53) yine uzunluğu yeniden kontrol etmeden ilk sıfır-uzunluklu SKB’den skb_peek_next()’e geçti. İki ardışık sıfır-uzunluklu SKB olduğunda, fonksiyon ikinci boş SKB’yi döndürdü; unix_stream_read_generic() bunu tekrar manage_oob() çağırmadan atladı, böylece gerçek OOB SKB dequeue edilip free edilirken u->oob_skb hâlâ ona işaret ediyordu.
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() tarafından açığa çıkarılan ilkel işlemler
- 1 bayt keyfi okuma (tekrarlanabilir):
state->recv_actor()nihayetindecopy_to_user(user, skb_sourced_addr, 1)yapar. Eğer dangling SKB saldırgan kontrollü belleğe (veya pipe page gibi kontrollü bir alias’a) yeniden tahsis edilirse, herrecv(MSG_OOB | MSG_PEEK)__check_object_size()ile izin verilen rastgele bir kernel adresinden bir baytı kullanıcı alanına çökmeden kopyalar.MSG_PEEKaçık tutulursa dangling pointer sınırsız okumalar için korunur. - Kısıtlı yazma:
MSG_PEEKkapalı olduğundaUNIXCB(oob_skb).consumed += 1offset0x44’teki 32-bit alanı artırır. 0x100 hizalı SKB tahsislerinde bu, 8 bayt hizalı bir kelimenin dört bayt üstünde yer alır; bu da ilgili offset0x40’ta barınan kelimeyi +4 GiB artırma primitifine çevirir. Bunu kernel write’a dönüştürmek için hassas 64-bit değeri o offset’e yerleştirmek gerekir.
Keyfi okuma için SKB sayfasını yeniden tahsis etme
- Order-0/1 unmovable freelist’leri boşaltma: Büyük bir read-only anonymous VMA map’leyin ve her sayfayı fault ederek page-table tahsisini zorlayın (order-0 unmovable). Page table’larla RAM’in ~%10’unu doldurmak, sonraki
skbuff_head_cachetahsislerinin order-0 listeleri tükendiğinde taze buddy sayfaları almasını sağlar. - SKB spreyleme ve bir slab sayfası izole etme: Birkaç düzine stream socketpair kullanın ve her socket için yüzlerce küçük mesaj (~0x100 byte/ SKB) kuyruğa koyarak
skbuff_head_cache’i doldurun. Seçilen SKB’leri free ederek hedef slab sayfayı tamamen saldırgan kontrolüne sokun ve ortaya çıkan okuma primitifiyle onunstruct pagerefcount’unu izleyin. - Slab sayfayı buddy allocator’e geri verme: Sayfadaki tüm objeleri free edin, sonra sayfayı SLUB per-CPU partial list’leri ve per-CPU page list’lerden iteleyecek kadar ilave allocation/free yapın ki sayfa buddy freelist’inde order-1 sayfa haline gelsin.
- Pipe buffer olarak yeniden tahsis etme: Yüzlerce pipe oluşturun; her pipe en az iki 0x1000-byte veri sayfası rezerv eder (
PIPE_MIN_DEF_BUFFERS). Buddy allocator bir order-1 sayfayı böldüğünde, yarılardan biri freed SKB sayfasını yeniden kullanır. Hangi pipe ve hangi offset’inoob_skbile alias ettiğini bulmak için pipe sayfalarının her yerine sahte SKB’lerde benzersiz marker byte’ları yazın ve marker dönene kadar tekrar edenrecv(MSG_OOB | MSG_PEEK)çağrıları yapın. - Stabil SKB düzeni oluşturma: Alias’lanan pipe sayfasını
data/headpointer’ları veskb_shared_infoyapısı ilgilenilen rastgele kernel adreslerine işaret eden sahtestruct sk_buffile doldurun. Çünkü x86_64copy_to_user()içinde SMAP’i devre dışı bırakır, kullanıcı mod adresleri kernel pointer’ları bilinene kadar staging buffer olarak kullanılabilir. - usercopy sertleştirmesine dikkat edin: Kopya
.data/.bss, vmemmap girişleri, per-CPU vmalloc aralıkları, diğer thread’lerin kernel stack’leri ve yüksek-order folio sınırlarını aşmayan direkt-map sayfalarına karşı başarılı olur..textveya__check_heap_object()tarafından reddedilen özel cache’lere karşı okumalar sadece-EFAULTdöndürür, process’i öldürmez.
Okuma primitifini kullanarak allocator’ları inceleme
- KASLR’ı kırma: Sabit mapping’teki
CPU_ENTRY_AREA_RO_IDT_VADDR(0xfffffe0000000000) adresinden herhangi bir IDT descriptor’ünü okuyun ve bilinen handler offset’ini çıkararak kernel base’i bulun. - SLUB/buddy durumu: Global
.datasembollerikmem_cachebazlarını açığa çıkarırken, vmemmap girişleri her sayfanın tip flag’lerini, freelist pointer’ını ve sahip cache’i gösterir. Per-CPU vmalloc segmentlerini taramakstruct kmem_cache_cpuörneklerini ortaya çıkarır, böylece önemli cache’lerin (ör.skbuff_head_cache,kmalloc-cg-192) sonraki tahsis adresi öngörülebilir olur. - Page table’lar:
mm_structokumak yerine (usercopy tarafından engellenir), globalpgd_list(struct ptdesc) içinde dolaşıp mevcutmm_struct’ucpu_tlbstate.loaded_mmile eşleştirin. Rootpgdbilindikten sonra, primitif pipe buffer’lar, page table’lar ve kernel stack’ler için PFN’leri eşlemek üzere her page table’ı gezebilir.
SKB sayfasını üst kernel-stack sayfası olarak yeniden kullanma
- Kontrol edilen pipe sayfasını tekrar free edin ve vmemmap ile onun refcount’unun sıfıra döndüğünü doğrulayın.
- Hemen dört yardımcı pipe sayfası allocate edin ve sonra buddy allocator’in LIFO davranışı deterministik olacak şekilde bunları ters sırada free edin.
- Bir yardımcı thread üretmek için
clone()çağırın; Linux stack’leri x86_64’de dört sayfadır, bu yüzden en son free edilen dört sayfa onun stack’i olur ve son free edilen sayfa (eski SKB sayfası) en yüksek adreslerde yer alır. - Yardımcı thread’in üst stack PFN’sinin recycled SKB PFN’si ile eşit olduğunu page-table walk ile doğrulayın.
- Arbitrary read’i kullanarak stack düzenini gözleyin ve thread’i
pipe_write()içine yönlendirin.CONFIG_RANDOMIZE_KSTACK_OFFSETher syscall içinRSP’den hizalanmış 0x0–0x3f0 rastgele bir değer çıkarır; tekrarlayan yazmalar ile diğer thread’tenpoll()/read()kombinasyonu yazarın istenen offset ile bloke olduğu zamanı açığa çıkarır. Şanslıysanız, spilledcopy_page_from_iter()bytesargümanı (R14) recycled sayfa içindeki offset0x40’ta yer alır.
Stack üzerinde sahte SKB metadata’sı yerleştirme
- AF_UNIX datagram socket üzerinde
sendmsg()kullanın: kernel kullanıcısockaddr_un’u stack-residentsockaddr_storageiçine (maks 108 byte) ve ancillary datayı syscall bloke olana kadar başka bir on-stack buffer’a kopyalar. Bu, stack bellekte hassas bir sahte SKB yapısı yerleştirmeye izin verir. - Kopyanın ne zaman bittiğini, unmapped bir user sayfasında yer alan 1 baytlık control message vererek tespit edin;
____sys_sendmsg()bunu fault ettirir, böylece o adresimincore()ile polling yapan yardımcı thread hedef sayfanın hazır olduğunu öğrenir. CONFIG_INIT_STACK_ALL_ZERO’dan gelen sıfır-initialize edilmiş padding kullanılmayan alanları doldurarak ek yazma yapmadan geçerli bir SKB header’ı tamamlar.
Self-looping frag list ile +4 GiB artışı zamanlama
skb_shinfo(fakeskb)->frag_list’i ikinci bir sahte SKB’ye işaret edecek şekilde düzenleyin (attacker-kontrollü user bellek içinde) ve bununlen = 0venext = &selfolmasını sağlayın.skb_walk_frags()bu listeyi__skb_datagram_iter()içinde iter’elediğinde iterator NULL’a ulaşmadığı için yürütme sonsuz döngüye girer ve kopya döngüsü ilerlemez.- İkinci sahte SKB’nin self-loop yapmasına izin vererek recv syscall’unu kernel içinde çalışır durumda bırakın. Artışı tetikleyeceğiniz zaman ikinci SKB’nin
nextpointer’ını user space’tenNULLyapın. Döngü çıkacak veunix_stream_recv_urg()hemenUNIXCB(oob_skb).consumed += 1çalıştırarak recycled stack sayfasında şu anki offset0x40’taki nesneyi etkileyecektir.
userfaultfd olmadan copy_from_iter()’i durdurma
- Devasa bir anonymous RW VMA map’leyin ve tamamen fault edin.
madvise(MADV_DONTNEED, hole, PAGE_SIZE)ile tek sayfalık bir delik açın ve bu adresiwrite(pipefd, user_buf, 0x3000)için kullanılaniov_iteriçine koyun.- Paralel olarak, başka bir thread’ten tüm VMA üzerinde
mprotect()çağırın. Bu syscall mmap write lock’u alır ve tüm PTE’leri gezer. Pipe writer delikteki sayfaya geldiğinde page fault handlermprotect()tarafından tutulan mmap lock’u bekler; bu, spilledbytesdeğeri recycled SKB sayfası tarafından host edilen stack segmentinde ikencopy_from_iter()’i deterministik bir noktada duraklatır.
Artışı keyfi PTE yazmalarına dönüştürme
- Artışı tetikleyin: Frag loop’u
copy_from_iter()duraklatılmışken serbest bırakın, böylece +4 GiB artışbytesdeğişkenine isabet etsin. - Kopyayı taşırma: Fault tekrar devam edince,
copy_page_from_iter()mevcut pipe sayfasına >4 GiB kopyalayabileceğini düşünür. Meşru 0x2000 byte’ı (iki pipe buffer) doldurduktan sonra bir iter daha yapar ve kalan kullanıcı verisini pipe buffer PFN’sinin ardından gelen fiziksel sayfaya yazar. - Komşuluğu düzenleyin: Allocator telemetrisi kullanarak buddy allocator’in süreç-sahipli PTE sayfasını hedef pipe buffer sayfasının hemen sonuna yerleştirmesini zorlayın (ör. pipe sayfalarını allocate etmek ve yeni virtual aralıkları touch etmek arasında geçiş yaparak page-table tahsisini tetikleyin ta ki PFN’ler aynı 2 MiB pageblock içinde hizalansın).
- Page table’ları overwrite edin: OOB
copy_from_iter()’in komşu sayfayı saldırganın seçtiği entry’lerle doldurması için ekstra 0x1000 byte user verisinde istenen PTE entry’lerini kodlayın; bu kernel fiziksel belleğinin RW/RWX user mapping’leri verilmesini veya mevcut entry’lerin SMEP/SMAP’i devre dışı bırakacak şekilde yeniden yazılmasını sağlayabilir.
Mitigations / hardening fikirleri
- Kernel:
32ca245464e1479bfea8592b9db227fdc1641705uygulanmalı (SKB’leri doğru şekilde yeniden doğrular) ve AF_UNIX OOB yalnızca gerçekten gerekliyse etkin olacak şekildeCONFIG_AF_UNIX_OOBile devre dışı bırakma düşünülmelidir (5155cbcdbf03).manage_oob()ek sağlamlık kontrolleriyle (örn.unix_skb_len() > 0olana dek döngü) sertleştirilmeli ve diğer socket protokolleri benzer varsayımlar için denetlenmelidir. - Sandboxing: seccomp profillerinde veya üst seviye broker API’lerinde
MSG_OOB/MSG_PEEKbayraklarını filtreleyin (Chrome değişikliği6711812artık renderer tarafındaMSG_OOB’u engelliyor). - Allocator savunmaları: SLUB freelist randomizasyonunu güçlendirmek veya per-cache page coloring uygulamak deterministik sayfa geri dönüşümünü zorlaştırır; pipe buffer sayısını sınırlamak da yeniden tahsis güvenilirliğini azaltır.
- İzleme: Yüksek oranda page-table tahsisi veya anormal pipe kullanımı gibi durumları telemetri ile açığa çıkarın — bu exploit büyük miktarda page table ve pipe buffer tüketir.
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 Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.


