Packet socket race-within-a-race (CVE-2025-38617)¶
AF_PACKET'te
packet_set_ring()ilepacket_notifier()arasında,po->num'ı koşullu sıfırlamaya dayanan bir race; ring buffer üzerinde bir use-after-free verir ki unprivileged bir kullanıcı (userns üzerinden CAP_NET_RAW) bunu tam bir privilege escalation'a zincirleyebilir.
Mechanism¶
Note
packet_set_ring() bir ring'i tear down ettiğinde veya reconfigure ettiğinde, tüm
critical section boyunca socket'i packet delivery'den unhooked tutmalıdır. Orijinal kod,
po->num'ı (bound protocol number) yalnızca socket zaten çalışıyorken sıfırlardı:
Bu noktada interface down idiyse, po->num non-zero değerini korur.
packet_set_ring(), po->bind_lock'u bıraktıktan sonra, eşzamanlı bir NETDEV_UP event'i
packet_notifier()'ı çalıştırır, non-zero bir po->num görür ve register_prot_hook()'u
çağırır — ring free edilirken/reconfigure edilirken socket'i delivery için yeniden hook'lar.
Packet'ler sonra tpacket_rcv()'a ulaşır; o da packet_set_ring()'in eşzamanlı free ettiği
bir ring buffer'a yazar: page-level bir use-after-free.
Bug, Linux 2.6.12'ye (2005) dayanır ve 6.16'da düzeltildi.
Walkthrough¶
"A race within a race", public exploit'in (Quang Le / Calif, Google kernelCTF) zincirlediği iki iç içe pencereyi tanımlar:
- Outer race —
packet_set_ring()'inpo->bind_lock'u bırakması ile sonraki lock'u yeniden alması arasında,po->numsıfırlanmadığı içinpacket_notifier()socket'i yeniden hook'lar. - Inner race — socket yeniden hook'lanmışken, gelen bir packet
tpacket_rcv()'ı tam olarakpacket_set_ring()ring'i free ettiği anda ring'e yazmaya sürer.
Nanosaniyelik bir pencereyi kullanılabilir hale getirmek için exploit onu genişletir (barrier-tarzı
bir trick): bir ring-buffer lock holder'ı uyut ki "lock release ile sonraki acquisition arasındaki
süre öngörülebilir ve esnetilebilir hale gelsin" — örn. SO_SNDTIMEO üzerinden tpacket_snd()'i
~1s'lik bir wait_for_completion_interruptible_timeout() boyunca po->pg_vec_lock'u tutmaya zorlayarak.
Writeup'ta raporlanan aşamalar
- Stage 0 — pencereyi genişlet sleeping-lock-holder barrier'ı ile.
- Stage 1 — heap overflow: free edilen ring'i daha küçük block'larla reclaim et; stale
metadata,
tpacket_rcv()'ın bir block'u aşıp yazmasına neden olur; bitişik birsimple_xattrsize field'ına 8-byte'lık bir write konar (8192 → 65536) ve oversized bir read/write oluşur. - Stage 2/3 — pgv overlap: bir
pgvarray girdisini bir kernel address'i ile overwrite et ve iki overlapping ring buffer inşa et (block'ları bir "puppet" buffer'ın pgv'sini overlay eden bir "master"); mmap'lenen ring üzerinden arbitrary page-aligned kernel R/W verir. - Stage 4 — KASLR bypass: bir pipe buffer array'i leak'le,
anon_pipe_buf_ops'u bul, kernel base'i türet. - Stage 5 — privesc:
__do_sys_kcmp()handler'ını, caller'ın cred'leriniinit_cred'e ve fs'iniinit_fs'e swap eden shellcode ile overwrite et.
Warning
Exploit modern hardening'i açıkça yener: CONFIG_RANDOM_KMALLOC_CACHES bypass edilir çünkü her
iki pgv array'i de alloc_pg_vec() içindeki aynı kcalloc()'tan gelir (özdeş cache hash);
ve CONFIG_SLAB_VIRTUAL, same-type reclamation (ring buffer'ların ring buffer'ları reclaim
etmesi) kullanılarak bypass edilir, böylece virtual-address aralıkları tutarlı kalır.
Detection¶
NETDEV_UP churn'ü ile eşzamanlı ring reconfiguration, namespaced unprivileged context'lerden
AF_PACKET kullanımı ve SO_SNDTIMEO + blocking tpacket_snd() pattern'leri anomalidir.
Mitigation¶
- Upstream'de (commit
01d3c8417b9c) state update'i unconditional yaparak düzeltildi —was_runningcheck'inden önce her zamanWRITE_ONCE(po->num, 0)— böylecepacket_notifier()her zamanpo->num == 0görür ve pencere sırasında yeniden hook'layamaz. CAP_NET_RAWentrypoint'ini reddetmek için unprivileged user namespace'leri engelle; kernel'leri >= 6.16 tut.
References¶
- A Race Within A Race: Exploiting CVE-2025-38617 (Calif blog)
- CVE-2025-38617 (Ubuntu security tracker)
- CVE-2025-38617 (SUSE security)
- CVE-2025-38617 (NVD) — fix commit
01d3c8417b9cdoğrulandı.