FUSE race-window widening primitive¶
Bloklayan bir FUSE
readhandler'ı kullanarak bir kernel thread'ini tam ocopy_from_user/copy_from_sockptrinstruction'ı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.