DirtyCred credential swap¶
Convert any UAF/double-free into privilege escalation by freeing an unprivileged credential object and letting the kernel reallocate a privileged one of the same type into the freed slot — no data field is overwritten.
Mechanism¶
DirtyCred (Lin, Wu, Xing — CCS 2022) generic, data-only bir tekniktir: tüm bir credential object'i aynı türden privileged bir tanesiyle takas eder. Kullandığı gerçek şu — kernel privilege'ı iyi bilinen heap object'lerinde tutar ve free edilmiş slot'ları tahmin edilebilir biçimde yeniden kullanır. İki object ailesi hedeflenir:
struct cred
task_struct'tan referans verilir, uid/gid gibi alanları tutar. DirtyCred mevcut process'in unprivileged cred'ini free eder, sonra kernel'i aynı slot'a privileged bir cred allocate etmeye zorlar. cred object'leri özel cred_jar slab cache'inde yaşar (GFP_KERNEL_ACCOUNT ile allocate edilir); privileged bir cred, bir setuid program çalıştığında ve kernel commit_creds()'i root uid ile çağırdığında üretilir. Dangling reference artık bir root credential'a işaret eder.
struct file
Burada f_mode (FMODE_WRITE/FMODE_READ) istismar edilir. vfs_write() içindeki f_mode & FMODE_WRITE kontrolü write işleminden önce gerçekleşir. DirtyCred yazılabilir bir dosya açar (kontrolü geçer), yavaş bir write başlatır, ardından race window içinde o yazılabilir file'ı free edip read-only hassas bir dosya (örneğin /etc/passwd) açar; böylece onun file'ı free edilen slot'a düşer — devam etmekte olan write artık read-only dosya üzerinde çalışır.
DirtyCred herhangi bir KASLR/heap-pointer leak gerektirmez ve kernel'ler/mimariler arasında taşınabilir.
Bu not vs. file-object swap
Bu not generic DirtyCred tekniğini ve özellikle struct cred swap path'ini (cred_jar, commit_creds(), setuid reclaim) kapsar. struct file swap'ın derinlemesine mekaniği — filp_cache, 4.13 öncesi/sonrası layout farkı, inode-lock vs userfaultfd race window'u, symlink/FMODE_ATOMIC_POS numarası — ayrı DirtyCred file-object swap not'unda işlenir.
Temel fikir tek bir slab slot'un sahipliğinin (privilege) takas edilmesidir — alanlar değil, object'in kimliği değişir (kavramsal şema, gerçek slab cache düzeni kernel'e göre değişir):
slab slot (cred_jar / filp)
before: [ unprivileged object ] <- dangling ref points here
|
| free() (bug ile tetiklenir)
v
after free: [ free slot ] <- dangling ref hala aynı adresi gösterir
|
| reclaim: setuid binary / open(O_RDONLY)
v
reclaimed: [ privileged object ] <- dangling ref artık privileged'a bakar
(root cred / read-only file)
Walkthrough¶
The struct cred variant:
1. Hold a dangling pointer to a cred object (from the UAF/double-free).
2. Free the current process's unprivileged cred.
3. Run a setuid binary (e.g. su / mount) so commit_creds() allocates a
root cred from cred_jar into the reclaimed slot.
4. The dangling pointer now references a root credential -> the process
acts as uid 0.
The struct file variant:
1. open() a writable file; vfs_write()'s f_mode & FMODE_WRITE check passes.
Begin a write via writev().
2. Stall the write inside the kernel after the check using userfaultfd
(page-fault hang) or FUSE / filesystem lock delays.
3. Trigger the bug to FREE the writable struct file.
4. open("/etc/passwd", O_RDONLY) -> a read-only struct file lands in the
freed slot (filp/file slab cache).
5. The stalled write resumes and writes attacker bytes through the
read-only file's struct file -> /etc/passwd is overwritten.
CVE-2021-4154 (cgroup v1 fsconfig UAF) ve CVE-2022-2588 (route4_change() içinde double-free) üzerinde gösterilmiştir.
Mitigation¶
Makale, privileged credential object'leri unprivileged olanlardan izole etmeyi önerir — ayrı cache'ler / guard page'li vmalloc — böylece free edilmiş bir unprivileged object'in slot'u privileged bir tanesi tarafından reclaim edilemez. Tespit zordur çünkü teknik data-only'dir ve çok az iz bırakır.