Skip to content

net/sched cls_tcindex UAF (CVE-2023-1829)

tcindex traffic-control classifier'ında bir double-free / use-after-free: bir "perfect hash" filter'ını silmek, filter'ı deactivate etmeden onun tcf_exts action'larını free eder, böylece stale pointer teardown sırasında ikinci kez free edilir — bir unprivileged local root primitive'i.

Mechanism

Note

tcindex (traffic control index) classifier'ı, filter'ları ya bir hash table'ında ya da küçük bir mask/hash key range için, tcindex_filter_result struct'larından oluşan bir perfect hash array'inde saklar. Her result, bir actions pointer'ı tutan bir tcf_exts gömer. tcindex_delete()'in (1) filter'ı lookup yapısından detach etmesi ve (2) result'ı RCU-free etmesi gerekir. Perfect-hash filter'lar için (1)'i atlar: filter asla deactivate edilmez, yine de exts->actions array'i tcf_exts_destroy() üzerinden free edilir. Classifier hâlâ o result'a canlı bir reference tutar. Chain/qdisc daha sonra flush edildiğinde (tcindex_destroy()tcf_exts_destroy()), aynı actions allocation'ı tekrar free edilir. İhlal edilen invariant standart "free edilmiş bir object unreachable olmalıdır"dır: burada free edilmiş bir allocation, deactivate edilmemiş perfect-hash slot'u üzerinden reachable kalır ve bir GFP_KERNEL kmalloc cache'inde kontrol edilebilir bir double-free → use-after-free verir.

Imperfect vs perfect path

Imperfect-hash path'i result'ları linked-list hash table p->h içinde tutar ve destruction'dan önce unlink eder (tutarlı). Sadece perfect-hash path'i (p->perfect doğrudan index'lenen array) result'ı deactivate etmeyi atlar, bu yüzden yalnızca o path freed bir object'i reachable bırakır. İkinci free, teardown'da şu zinciri izler: tcf_chain_flush → tcf_proto_destroy → tcindex_destroy → tcindex_destroy_rexts_work → tcf_exts_destroy → kfree(exts->actions).

Walkthrough

Classifier, bir unprivileged user namespace'inden (yeni bir userns içinde CAP_NET_ADMIN) reachable'dır. Küçük bir mask seçilerek bir perfect hash seçilir, böylece key space perfect-hash threshold'una sığar.

/* create a netns we control, gaining CAP_NET_ADMIN over it */
unshare(CLONE_NEWUSER | CLONE_NEWNET);

Sonra perfect hash path'i alınsın diye küçük hash/mask ile bir qdisc ve bir tcindex filter oluşturun, bir action attach edin ve ilk free'i tetiklemek için onu silin:

tc qdisc add dev lo root handle 1: htb
# small mask => perfect hash; attach an action so exts->actions is allocated
tc filter add dev lo parent 1: handle 1 protocol ip prio 1 \
   tcindex mask 0x1 shift 0 \
   action gact drop
# delete the perfect-hash filter -> tcindex_delete() frees exts->actions
tc filter del dev lo parent 1: handle 1 protocol ip prio 1 tcindex

Free edilen chunk kmalloc-0x100'e düşer (exts->actions tarafından kullanılan GFP_KERNEL cache'i). İkinci free'den önce, slot'u attacker-shaped bir object ile reclaim edin.

STAR Labs exploitation chain'i (CVE-2023-1829)
  1. Reclaim: free edilen 0x100 chunk'ını nft table udata (user data) ile reclaim edip dangling allocation'a bir write kazanın.
  2. Leak: bir nft_object (counter ops) ile reclaim ederek heap ve kernel base'i leak edin: nft_obj_dump(), nft_object.ops / list pointer'larını okur ve kmalloc ile image adreslerini açığa çıkarır.
  3. Control flow: kontrollü heap'e işaret eden sahte bir nft_object.ops forge edin, onu nft_obj_dump() üzerinden çağırın, sonra modprobe_path'i overwrite edin, böylece bad-magic bir dosyanın bir sonraki execve()'i bir attacker script'ini root olarak çalıştırsın.

Chain'i flush etmek ikinci free'i tetikler (tcindex_destroy()tcf_exts_destroy()) ve zaten reclaim edilmiş object üzerinde UAF'ı tamamlar.

Çıplak bir double-free PoC, tcf_exts_destroy / kfree içinde bir kernel BUG/KASAN double-free splat'i üretir.

Detection

  • KASAN, tcindex path'inden tcf_exts_destroy() kaynaklı use-after-free / double-free raporlar.
  • Unprivileged user namespace'leri içinden tc filter add/delete üzerine audit/eBPF telemetrisi güçlü bir sinyaldir; perfect-hash trigger'ı, hemen ardından delete + chain flush gelen küçük-mask'li bir tcindex filter add gerektirir.

Mitigation

  • Commit 8c710f75256bb3cf05ac7b1672c82b92c43f3d28 ile düzeltildi; fix nihayetinde TCINDEX classifier'ını kernel'den tamamen kaldırdı.
  • Defense in depth: kernel.unprivileged_userns_clone=0 (ya da user.max_user_namespaces=0) set edin ve attack surface'i kaldırmak için CONFIG_NET_CLS_TCINDEX'i disable edin.

References