Skip to content

DirtyCred file-object swap

The specific swap primitive of DirtyCred: free a writable struct file mid-write and reallocate the freed slot with a privileged read-only file so the kernel's approved write redirects into the privileged file.

Mechanism

Bu not vs. credential swap

Bu not yalnızca struct file swap primitive'inin düşük seviye mekaniğine (filp_cache, kernel layout, race window'ları) odaklanır. Generic DirtyCred tekniği ve struct cred / root-cred reclaim path'i için bkz. DirtyCred credential swap.

Note

Tek cümleyle: yazılabilir bir dosyanın struct file'ını write-permission kontrolü geçtikten sonra ama data yazılmadan önce free et, sonra aynı slab slot'unu privileged read-only bir dosyanın struct file'ı ile reclaim et; böylece bekleyen write privileged dosyaya düşer. Write'ın kendisi zaten yazılabilir dosyanın f_mode'una (FMODE_WRITE / FMODE_CAN_WRITE) karşı onaylanmıştı; swap, o kontrol ile asıl data store arasındaki boşluğu istismar eder.

struct file, özel filp_cache slab'ından (sembol filp_cachep) allocate edilir. İki kernel layout'u önemlidir:

  • Linux 4.13'ten önce vfs_writev permission-check -> import_iovec -> write sırasını izlerdi, dolayısıyla import_iovec sırasındaki bir userfaultfd page-fault, kontrol ile write arasında yürütmeyi duraklatırdı (early userfaultfd-based race yaklaşımı).
  • 4.13'ten sonra import_iovec kontrolün önüne taşındı, dolayısıyla exploit filesystem katmanına kayar: generic_perform_write, filesystem write'ından önce kullanıcı page'ini iov_iter_fault_in_readable ile pre-fault eder — userfaultfd'lenebilir bir nokta — ya da daha sağlam olarak, aynı dosyaya yazan iki writer ext4 inode lock'unda (ext4_buffered_write_iter içindeki inode_lock) serileşir, bu da çok saniyelik bir swap window verir.

Walkthrough

// Writer A holds the inode lock with a slow 4GB write (~dozens of seconds on HDD)
int big = open("/tmp/x", O_WRONLY);
write(big, buf, 4UL<<30);

// Writer B: victim writable file object — opened via a softlink so it lacks
// FMODE_ATOMIC_POS and does not stall in __fdget_pos on the shared position lock
int fd = open("/tmp/x_symlink", O_WRONLY);   // struct file -> filp_cache
write(fd, payload, len);                      // FMODE_WRITE check passes, blocks on inode_lock

// Free B's file via the heap bug, then place a privileged read-only file in the slot
trigger_uaf_free_file();                       // dangling filp_cache slot
int p = open("/etc/passwd", O_RDONLY);         // reclaims slot with read-only file

// A releases inode_lock -> B's approved write now stores into /etc/passwd

Warning

Literatürde gösterilen write hedefi /etc/passwd'dir (hacker:x:0:0:root:/:/bin/sh enjekte edilir); bir SUID binary'sinin içeriğini file-swap ile overwrite etmek belgelenmiş bir yol değildir — SUID istismarı cred variant'ına aittir (root bir cred allocate etmek için su, mount, pkexec vb. çalıştırmak). f_cred/f_pos standart alanlardır ama makaleden birebir değildir; yalnızca f_mode/FMODE_* öyledir.

Aynı dosyanın double-open'ı file refcount'unu artırır, bu normalde __fdget_pos içindeki FMODE_ATOMIC_POS position lock'unu devreye sokardı; writer B'yi bir symlink üzerinden açmak bu lock'tan kaçınır, böylece thread yalnızca inode lock'unda stall olur — amaçlanan race window.

Mitigation

DirtyCred'in önerdiği savunma, yazılabilir file object'lerini (ve root cred'i) vmalloc bölgesine izole eder (__alloc_file -> vzalloc, file_free_rcu -> kvfree); böylece free edilmiş yazılabilir-dosya page'leri asla unprivileged direct-mapped pool'a geri dönmez ve swap kırılır. Upstream bunun yerine reclaim'i daha az deterministik yapmak için slab-cache ayrımına ve freelist hardening'e dayanır.

References