Skip to content

FUSE race-window widening primitive

Bloklayan bir FUSE read handler'ı kullanarak bir kernel thread'ini tam o copy_from_user/copy_from_sockptr instruction'ında dondur; böylece küçük bir double-fetch/TOCTOU window'unu istediğin kadar uzun bir window'a dönüştür.

Mechanism

Bu, FUSE-based exploitation demand-paging hook'unun özellikle bir window widener olarak uygulanmasıdır. Race-prone kernel kodu (bir double-fetch ya da bir lock drop boyunca check-then-use), iki memory access'inin wall-clock zamanında etkin biçimde atomik olduğunu varsayar. Bu access'lerden biri FUSE-backed bir page'i hedefliyorsa, access'in ne zaman tamamlanacağına attacker'ın read callback'i karar verir.

Note

Invariant şu: kernel'in bir user buffer'ı check'i ile use'u arasında, attacker page fault'u stall ederek keyfi bir gecikme enjekte edebilir. Bu gecikme sırasında ikinci bir thread ilgili state'i mutate eder, dolayısıyla stall'lanan access devam ettiğinde attacker'ın değiştirdiği data üzerinde işlem yapar. Race artık probabilistic değildir — window'u attacker kontrollüdür.

Warning

Bunun için FUSE, userfaultfd'ye tercih edilir çünkü userfaultfd "doesn't catch remote memory accesses": kernel'in başka bir context adına aldığı fault'lar userfaultfd'ye iletilmez, ama FUSE onlara yine de hizmet eder.

Walkthrough

CVE-2021-32606 (CAN ISOTP) kanonik örnektir — isotp_setsockopt() ile isotp_bind() arasında bir TOCTOU. Option buffer'ını FUSE'tan map'le ki kernel'in kopyası fault etsin:

fuse_fd = open("mnt/hello", O_RDWR);
opt = mmap(NULL, sizeof(struct can_isotp_options),
           PROT_READ|PROT_WRITE, MAP_SHARED, fuse_fd, 0);
/* thread A: setsockopt(sk, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, opt, len)
 *           → kernel blocks in copy_from_sockptr() inside the FUSE read handler
 * thread B: isotp_bind() completes and registers the receiver
 * resume A: the copied option (CAN_ISOTP_SF_BROADCAST) lands on the bound socket */

hello_read() handler'ı ~2 sn uyur ve kernel'i copy_from_sockptr()'de dondurur; yarışan isotp_bind() sonuna kadar çalışır; ardından devam eden kopya kötü niyetli flag'i zaten bind edilmiş socket'e yazar.

İlgili aile üyesi

Project Zero'nun "Exploiting Recursion in the Linux Kernel" çalışması, bir mapping chain'inin sonuna bir FUSE mapping'i yerleştirerek recurse eden bir kernel thread'ini stack-overwrite'ın ortasında dondurur, sonra FUSE server'ını öldürerek devam ettirir — aynı widening fikri, farklı bir bug sınıfına karşı.

Detection

Kernel tarafında, altta yatan double-fetch tespit edilebilir kusurdur; syzkaller'ın double-fetch detection tooling'i vardır. Runtime'da, security-sensitive bir syscall'a buffer besleyen bir FUSE mount gözlemlenebilir pattern'dir.

Mitigation

Kernel'da double-fetch'i ortadan kaldır: user data'yı bir kez kopyala, validate et, sonra kullan; check'ten sonra asla yeniden fetch etme. Unprivileged FUSE mount'larını kısıtlamak, widening primitive'inin kendisini ortadan kaldırır.

References