Skip to content

V4L2 vivid driver UAF (CVE-2019-18683)

V4L2 vivid test driver'ında stream-stop sırasındaki race condition'lar, hâlâ queue'da olan bir vb2_buffer'ı free eder ve /dev/video0 üzerinden local privilege escalation için istismar edilebilir bir kmalloc-1k use-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:

# attacker reads kernel log to harvest leaked register state
cat /dev/kmsg | grep -i 'RSP\|R11'

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-1k cache'inde vid_cap_buf_queue() / v4l2_device_release içinde use-after-free kanonik imzadır.
  • Davranışsal: birden çok CPU'dan /dev/video0 üzerinde hızlı open/read/close çalkalanması, setxattr/adjtimex spray'i ve /dev/kmsg kazı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