Android Binder use-after-free LPE (CVE-2023-20938)¶
Unaligned bir
offsets_size, binder'ın object processing'i atlamasına ama yine de failure cleanup'ını çalıştırmasına yol açar; birbinder_noderef'i double-decrement edilir ve bir transaction buffer hâlâ ona pointer tutarken node free edilir.
Mechanism¶
Fast path'in hiç işlemediği objeler için cleanup path çalışınca
Bir binder transaction buffer, bir payload'un yanında gömülü
flat_binder_object girdilerini (binder node'lar, handle'lar, fd'ler) tarif
eden bir object offset dizisi tutar. Başarıda da başarısızlıkta da
binder_transaction_buffer_release() bu offset'ler üzerinde gezer ve her
gömülü objenin aldığı reference'ları geri bırakır.
CVE-2023-20938, unaligned bir offsets_size altında acquire path ile
release path arasındaki bir asimetridir. binder_transaction() içindeki
normal processing loop alignment'ı doğrular ve hatalı input'ta binder
objelerini işleyecek (ve ref-increment edecek) for-loop'u atlar, failed_at
= 0 ve is_failure = true ile error handling'e atlar. Ama
binder_transaction_buffer_release() sonra off_end_offset'i buffer'ın sonu
olarak hesaplar ve buffer_offset hâlâ 0 iken, reference'ları hiç alınmamış
objeler üzerinde gezip onları "release" eder.
İhlal edilen invariant refcount eşlemesidir: her decrement, aynı obje
üzerinde önceki bir increment'le eşleşmek zorunda. Burada release path,
acquire path'in atladığı objeler için binder_node->local_strong_refs'i
decrement eder. İki BINDER_TYPE_BINDER objesi ile bu, kontrol edilebilir bir
double-decrement haline gelir. local_strong_refs sıfıra underflow ettiğinde
binder_dec_node_nilocked() true döner ve binder_free_node() struct
binder_node'u free eder — ama bir struct binder_buffer hâlâ dangling bir
target_node pointer'ı tutmaktadır. İşte use-after-free budur; oradan
sonrası, freed node'u kontrol edilen bir obje ile reclaim edip read + unlink
primitive'leri kurmak için bir heap-grooming/cross-cache egzersizidir.
Walkthrough¶
AndroidOffSec writeup'ının savunma amaçlı yeniden kurgusu; çalıştırılabilir bir exploit değil.
offsets_size'ı unaligned olan ve ikiBINDER_TYPE_BINDERobjesi taşıyan bir transaction sür; bu, atla-sonra-release dengesizliğini zorlar:
/* pseudo: build a BC_TRANSACTION with offsets_size not 8-aligned */
struct flat_binder_object objs[2] = { /* type = BINDER_TYPE_BINDER ... */ };
tr.offsets_size = sizeof(objs) - 4; /* unaligned -> skip processing loop */
ioctl(binder_fd, BINDER_WRITE_READ, &bwr);
-
Failure path
binder_transaction_buffer_release()'i çalıştırır,local_strong_refs'i double-decrement eder vebinder_free_node()binder_node'u free eder. Artık birbinder_buffer.target_nodedangling'dir. -
Freed
binder_node'u (etkilenen GKI kernel'lerdekmalloc-128içinde) kontrol edilen bir obje ile reclaim et. Linux 5.10eventpoll_epicache'iniSLAB_ACCOUNTolarak işaretlediği için (onukmalloc-128'den ayırır), exploit bir page-level cross-cache-attack kullanır:kmalloc-128slab'larını page allocator'a geri boşalt ki bireventpoll_epi(struct epitem) slab'ı page'i kapsın. -
Leak: freed node'un
ptr/cookie'sinibinder_thread_read()ile oku, böylece pointer'ları kurtar ve KASLR'ı kır. -
Unlink/write: kontrol edilen data ile reallocate et ve
BC_FREE_BUFFERilehlist_del(&node->dead_node)'u tetikle; bu kontrol edilen bir write verir. Ardından birstruct file/epitemilişkisini corrupt edipFIGETBSZgibi bir ioctl üzerinden okuyarak arbitrary read'e pivot et.
Beklenen son durum (AndroidOffSec exploitation)
[*] crafted unaligned transaction -> binder_node local_strong_refs underflow
[+] binder_node freed, binder_buffer.target_node dangling
[+] kmalloc-128 page reclaimed by eventpoll_epi (epitem) via cross-cache
[+] leaked node ptr/cookie -> KASLR defeated
[+] hlist_del unlink -> controlled write
[+] arbitrary kernel read via epitem/file confusion
[+] root + SELinux bypass
Detection¶
- KASAN,
binder_nodeuse-after-free'inibinder_free_node←binder_transaction_buffer_releaseüzerinden geçen bir free stack ile yakalar. offsets_size'ı 8-byte hizalı olmayan binder transaction'lar anormaldir ve driver sınırında işaretlenmesi ucuzdur.- Bir dizi
kmalloc-128free'sinin ardından gelen epoll (epitem) allocation baskısı, cross-cache grooming'in izidir.
Mitigation¶
- 2023-02-01 Android security patch level'ını uygula; ilk fix'in eksik
olduğunu ve ilgili
binder_transaction_buffer_releasehardening'inin (CVE-2023-21255) Temmuz 2023 bülteninde geldiğini unutma — Temmuz 2023'e kadar patch'le. - SLAB account ayrımı, freelist randomization ve page-allocator hardening, cross-cache reclaim'in maliyetini artırır.