Skip to content

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; bir binder_node ref'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.

  1. offsets_size'ı unaligned olan ve iki BINDER_TYPE_BINDER objesi 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);
  1. Failure path binder_transaction_buffer_release()'i çalıştırır, local_strong_refs'i double-decrement eder ve binder_free_node() binder_node'u free eder. Artık bir binder_buffer.target_node dangling'dir.

  2. Freed binder_node'u (etkilenen GKI kernel'lerde kmalloc-128 içinde) kontrol edilen bir obje ile reclaim et. Linux 5.10 eventpoll_epi cache'ini SLAB_ACCOUNT olarak işaretlediği için (onu kmalloc-128'den ayırır), exploit bir page-level cross-cache-attack kullanır: kmalloc-128 slab'larını page allocator'a geri boşalt ki bir eventpoll_epi (struct epitem) slab'ı page'i kapsın.

  3. Leak: freed node'un ptr/cookie'sini binder_thread_read() ile oku, böylece pointer'ları kurtar ve KASLR'ı kır.

  4. Unlink/write: kontrol edilen data ile reallocate et ve BC_FREE_BUFFER ile hlist_del(&node->dead_node)'u tetikle; bu kontrol edilen bir write verir. Ardından bir struct file/epitem ilişkisini corrupt edip FIGETBSZ gibi 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_node use-after-free'ini binder_free_nodebinder_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-128 free'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_release hardening'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.

References