net/sched cls_tcindex UAF (CVE-2023-1829)¶
tcindextraffic-control classifier'ında bir double-free / use-after-free: bir "perfect hash" filter'ını silmek, filter'ı deactivate etmeden onuntcf_extsaction'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)
- Reclaim: free edilen
0x100chunk'ınınfttableudata(user data) ile reclaim edip dangling allocation'a bir write kazanın. - 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 vekmallocile image adreslerini açığa çıkarır. - Control flow: kontrollü heap'e işaret eden sahte bir
nft_object.opsforge edin, onunft_obj_dump()üzerinden çağırın, sonramodprobe_path'i overwrite edin, böylece bad-magic bir dosyanın bir sonrakiexecve()'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,
tcindexpath'indentcf_exts_destroy()kaynaklıuse-after-free/double-freeraporlar. - Unprivileged user namespace'leri içinden
tcfilter add/delete üzerine audit/eBPF telemetrisi güçlü bir sinyaldir; perfect-hash trigger'ı, hemen ardından delete + chain flush gelen küçük-mask'li birtcindexfilter add gerektirir.
Mitigation¶
- Commit
8c710f75256bb3cf05ac7b1672c82b92c43f3d28ile düzeltildi; fix nihayetindeTCINDEXclassifier'ını kernel'den tamamen kaldırdı. - Defense in depth:
kernel.unprivileged_userns_clone=0(ya dauser.max_user_namespaces=0) set edin ve attack surface'i kaldırmak içinCONFIG_NET_CLS_TCINDEX'i disable edin.