Skip to content

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.

References