watch_queue OOB write "Blue Klotski" (CVE-2022-0995)¶
watch_queuenotification filter kodundaki tutarsız bit-width bounds check, gap aralığındaki bir filter type'ın allocation sizing'i bypass etmesine ama yine de kabul edilmesine izin veriyor; sonuçta public exploit'lerin page seviyesinde bir use-after-free'e groom ederek local privilege escalation'a çevirdiği bir slab out-of-bounds bit-set write ortaya çıkıyor.
CVE attribution — önce bunu oku
Bu girdinin alias listesi CVE-2021-3573'ü miras almış, ama o identifier aslında
Bluetooth HCI socket use-after-free'i (hci_sock_bound_ioctl() ile
hci_unregister_dev() arasındaki race) — farklı bir bug class ve subsystem. Burada
anlatılan watch_queue filter-notification OOB write'ı CVE-2022-0995'tir;
NVD/vendor advisory'leri ve Quarkslab write-up'ına karşı doğrulandı. İkisini sessizce
birbirine karıştırmak yerine CVE-2022-0995'i yetkili identifier olarak kullanıyor
ve alias uyuşmazlığını işaretliyoruz. (CVE numaraları uydurulmadı; her ikisi de
fetch edilip disambiguate edildi.)
Mechanism¶
Note
watch_queue_set_filter(), user'ın verdiği filter entry'lerini type_filter
bitmap'inin bit width'i konusunda anlaşamayan iki yerde validate eder. Bir loop
type'ı sizeof(type_filter) * 8 (bits-per-byte) ile sınırlarken gerçek set işlemi
BITS_PER_LONG-ölçekli bir limit kullanır; dolayısıyla iki limit arasındaki
gap'e düşen bir filter type, allocation'ı boyutlandıran accounting'i atlatır ama
__set_bit(type, type_filter) tarafından hâlâ aralık içinde gibi muamele görür.
İhlal edilen invariant şu: "set edilen her bit index, bitmap boyutlanırken sayılmıştı."
Sonuç, allocate edilmiş watch_type_filter object'inin sonunu aşan bir __set_bit
write'tır — küçük bir kmalloc slab içinde kontrollü, single-bit bir out-of-bounds
write.
Tek bitlik bir OOB set kendi başına zayıftır; gücü yanında ne durduğundan gelir. Eğer
overflow edilen object, bir kernel pointer tutan bir yapının (public çalışma onu bir
pipe_buffer yakınına yerleştirir) hemen bitişiğine groom edilirse, o pointer'daki tek
bir biti flip etmek onu yeniden hedefler.
Walkthrough¶
Public Quarkslab "PageJack" analizini takip eden yüksek seviyeli reproduction (sadece kavramsal parçalar — offset ya da gadget yok):
- Uyumsuzluğu tetikle.
type'ı gap aralığına düşen bir watch-queue filter gönder; böylece ikinci loop'u geçer ama hiç boyutlandırılmamıştır, bu da__set_bit'in small-slab object'in hemen ötesinde tek bir biti flip etmesine yol açar. - Komşuyu groom et. Spray yaparak bir
pipe_buffer'ın (birpagepointer'ı tutan) filter object'inin hemen ardına gelmesini sağla — shaping disiplini için bkz. heap-grooming-feng-shui ve kernel-low-fragmentation-heap-exploitation. - Pointer'ı bit-flip et. OOB
__set_bit, bitişiktekipipe_buffer.page'in bir bitini flip ederek onun farklı bir physical page'e alias yapmasını sağlar — artık iki pipe aynı page'e referans verir. - Bir page UAF üret. Bir pipe'ı free et; page geri verilir ama ikinci pipe hâlâ canlı bir reference tutmaktadır, böylece page seviyesinde bir use-after-free ortaya çıkar (page-uaf / puaf-primitive şekli).
- Reclaim et ve escalate et. Free edilen page'i bir target object ile spray et ve hayatta kalan reference üzerinden onu düzenleyerek privilege-ilgili alanları flip et, root shell ile bitir (bkz. page-spray).
İki limitin uyuşmazlığı, ruhen
Fix, her iki loop'u aynı upper bound üzerinde anlaşır hale getirir; böylece kabul edilen hiçbir bit index boyutlandırılmış bitmap'i aşamaz.Detection¶
- Source audit: bounds-check çarpanının allocation/sizing çarpanından farklı
olduğu (
* 8vs* BITS_PER_LONG) herhangi bir bitmap kodunu işaretle — bu off-by-bit-width sınıfının imzasıdır. Ağaçta, user'ın verdiği index'ler üzerinde dönen ve__set_bit'i besleyen eşleşik loop'ları ara. - Runtime / hardening:
CONFIG_SLAB_FREELIST_HARDENINGve redzoning ile (test fleet'lerinde KASAN, ya da canary host'larda production SLUB debug), OOB__set_bitredzone/bitişik metadata'yı bozar ve slab corruption olarak yakalanır. - Behavioural: exploit yoğun
pipe()/filter churn'ü ve unprivileged bir process'ten alışılmadık bir watch-queue filter syscall patlaması üretir; watch-queue kullanım telemetry'sini ani bir cred/uid geçişiyle eşleştirmek güçlü bir sinyaldir. - General: herhangi bir page-UAF LPE'sinde olduğu gibi, meşru bir setuid yolu olmadan gerçekleşen bir process credential değişimini izlemek en güvenilir backstop olarak kalır.
Mitigation¶
- Patch: upstream'de filter loop'ları aynı bound üzerinde anlaşır hale
getirerek düzeltildi; böylece kabul edilen bir
typeasla boyutlandırılmıştype_filterbitmap'ini aşan bir index'e denk gelemez. Fix stable tree'lere merge edildi (5.17 release'inden önce düzeltildi; geniş çapta backport edildi). Patch'li bir kernel'e güncelle. - Attack-surface reduction:
watch_queue, keyring/pipe notification interface'leri üzerinden erişilebilir; kullanılmadığı yerde,CONFIG_WATCH_QUEUEolmadan derlenen kernel'ler bu yüzeyi tamamen kaldırır. - Defence-in-depth: SLUB freelist hardening, randomised freelist'ler ve
init_on_free/init_on_alloc, page-spray/reclaim adımının maliyetini artırır;vm.unprivileged_userfaultfd=0ve benzerleri, bu exploit sınıfının dayandığı grooming primitive'lerini azaltır.