V4L2 vivid driver UAF (CVE-2019-18683)¶
V4L2
vividtest driver'ında stream-stop sırasındaki race condition'lar, hâlâ queue'da olan birvb2_buffer'ı free eder ve/dev/video0üzerinden local privilege escalation için istismar edilebilir birkmalloc-1kuse-after-free verir.
Mechanism¶
Neden çalışır
vivid driver'ı (drivers/media/platform/vivid), Ubuntu, Debian, Arch, SUSE ve
diğerleri tarafından bir loadable module olarak gönderilen bir software V4L2 capture
cihazıdır; yüklü olduğu yerde /dev/video0, oturum açmış kullanıcılar tarafından ACL'ler
üzerinden erişilebilir. Stream-stop yolları — vivid_stop_generating_vid_cap(),
vivid_stop_generating_vid_out() ve sdr_cap_stop_streaming() — kthread_stop()
çağırırken vivid_dev.mutex'i düşürür.
O unlock bir pencere açar: eşzamanlı bir vb2_fop_read() (cihaz üzerinde düz bir
read()), stop sırasında buffer queue'sunu manipüle edebilir. Race'i kazanan bir
saldırgan, stop yolu queue'nun yerleştiğine inandıktan sonra vb2_queue.queued_list'e
fazladan bir vb2_buffer ekler. __vb2_queue_free() sonra buffer'ları free ettiğinde
ya da streaming yeniden başladığında, driver zaten free edilmiş bir vb2_buffer'ı
dereference eder — kmalloc-1k cache'inde bir use-after-free, ilk olarak
linked-list manipülasyonu sırasında vid_cap_buf_queue() içinde KASAN tarafından
yakalandı. İstismar edilen invariant: buffer queue'sunun stream-stop boyunca stabil
olduğu varsayılır ama mutex işlem ortasında release edilir.
Free edilen object, ->vb2_queue->mem_ops->vaddr()'i daha sonra çağrılan bir
vb2_buffer olduğundan, slot'u kontrollü bir object ile reclaim etmek bir function
pointer hijack verir.
Walkthrough¶
Yalnızca yetkili test
Yalnızca sahip olduğun, vivid module'ü yüklü, etkilenen bir kernel (≤ 5.3.8)
çalıştıran bir VM üzerinde yeniden üret.
1. Stream-stop'u race et. Farklı CPU'lara pinlenmiş iki pthread, cihazın
open/read/close'unda döner ve vivid_stop_generating_vid_cap()'i capture kthread'i
vivid_thread_vid_cap()'e karşı race eder:
// thread A and thread B, each pinned to its own CPU
for (;;) {
int fd = open("/dev/video0", O_RDWR);
char buf[64];
read(fd, buf, 0xfffded); // drives vb2_fop_read into the stop window
close(fd);
}
2. Free edilmiş vb2_buffer'ı reclaim et (userfaultfd + setxattr spray). "xairy"nin
tekniğini kullanarak ~44 sprayer thread, user buffer'ı bir userfaultfd page'i ile
desteklenen setxattr() çağırır; böylece kontrollü vivid_buffer-şekilli object, tam
UAF slot'u reclaim edilebilir olduğunda kmalloc-1k içinde dondurulur:
// each sprayer: setxattr with a uffd-backed value of size ~1k
setxattr("/tmp/a", "user.x", uffd_page /*1024 bytes*/, 1024, 0);
// uffd handler holds the copy_from_user open to control reclaim timing
Altta yatan primitive'ler için bkz. userfaultfd race-window widening ve setxattr spray.
3. KASLR'ı leak et. Exploit, /dev/kmsg'den kernel uyarılarını parse eder, kernel
stack konumunu ve KASLR displacement'ını kurtarmak için sızdırılmış RSP/R11 register
değerlerini okur:
4. Sahte bir vb2_queue/vb2_mem_ops yerleştir ve control flow'u hijack et.
adjtimex() (yine userfaultfd üzerinden), mem_ops->vaddr'ı bir stack-pivot gadget'ına
(push rdi ; pop rsp) işaret eden bir fake vb2_queue içeren, hazırlanmış bir
timex-şekilli yapıyı kernel stack üzerinde dondurur. Free edilmiş vb2_buffer
kullanıldığında, vb2_buffer->vb2_queue->mem_ops->vaddr() bir kernel-stack ROP chain'ine
pivot eder.
5. Escalate et. ROP chain, /etc/passwd'yi yeniden yazan (root parolasını temizleyen)
bir script çalıştırmak için run_cmd()'yi çağırır, sonra temiz çıkmak için
do_task_dead()'i çağırır.
Beklenen etki: vulnerable bir kernel'de v4l2_device_release / vid_cap_buf_queue()
içinde KASAN-işaretli bir UAF write; weaponize edilmiş yolda ise bir root shell.
Detection¶
- KASAN:
kmalloc-1kcache'indevid_cap_buf_queue()/v4l2_device_releaseiçindeuse-after-freekanonik imzadır. - Davranışsal: birden çok CPU'dan
/dev/video0üzerinde hızlı open/read/close çalkalanması,setxattr/adjtimexspray'i ve/dev/kmsgkazıması ile birleşmiş.
Mitigation¶
- Patch: vivid stop yollarındaki mutex locking'i düzelt (upstream fix V4L2 vivid driver'a indi; fix'e ≥ bir kernel'e, 5.3.8 sonrasına güncelle).
vivid'i unload/blacklist et — production ihtiyacı olmayan bir test driver'ıdır; module'ü blacklist'lemek attack surface'i tamamen kaldırır.- Unprivileged
userfaultfd'yi kısıtla (vm.unprivileged_userfaultfd=0) ve/dev/kmsg'yi sıkılaştır (dmesg_restrict=1) ki reclaim ve KASLR-leak adımları kırılsın.
References¶
- Alexander Popov — CVE-2019-18683: Exploiting a Linux kernel vulnerability in the V4L2 subsystem — https://a13xp0p0v.github.io/2020/02/15/CVE-2019-18683.html
- NVD — CVE-2019-18683 — https://nvd.nist.gov/vuln/detail/CVE-2019-18683
- PT-2019-05 advisory (Positive Technologies) — https://www.ptsecurity.com/ww-en/analytics/threatscape/pt-2019-05/