Skip to content

cred struct overwrite

Yerinde root'a yükselmek için mevcut task'ın struct cred'inin uid/gid field'larını doğrudan sıfırla.

Mechanism

Her task_struct iki pointer tutar: const struct cred *real_cred (objective context — task'ın kim olduğu) ve const struct cred *cred (subjective context — kim olarak davrandığı). Credential yapısı (include/linux/cred.h) şöyle başlar:

struct cred {
    atomic_long_t   usage;
    kuid_t  uid;    /* real UID */
    kgid_t  gid;    /* real GID */
    kuid_t  suid;   kgid_t  sgid;
    kuid_t  euid;   kgid_t  egid;
    kuid_t  fsuid;  kgid_t  fsgid;
    unsigned securebits;
    kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset, cap_ambient;
    /* ... */
} __randomize_layout;

Note

Sekiz id field'ı (uidfsgid), 8-byte'lık usage refcount'un hemen ardından paketlenmiş 4-byte'lık kuid_t/kgid_t'lerdir. Bunları sıfırlamak, task'ı hem filesystem hem de permission check'leri için root yapar. Bu, commit_creds(prepare_kernel_cred(...))'in alternatifidir: yeni bir cred allocate etmek yerine mevcut olanı yerinde mutate edersin — bir AAW artı cred'i bulmanın bir yolunu gerektirir.

İlişki, kavramsal olarak (offset/boyutlar kernel build'ine göre değişir, gösterilen sıralama illüstratiftir):

 task_struct                 struct cred (slab object)
 +----------------+          +---------------------------+
 | ...            |          | usage   (refcount) <- DOKUNMA
 | real_cred  *---|----+     |---------------------------|
 | cred       *---|--+ |     | uid gid suid sgid         |  <- AAW ile
 | comm[]         |  | +---->| euid egid fsuid fsgid     |     hepsi 0 = root
 | ...            |  |       |---------------------------|
 +----------------+  |       | securebits / cap_* ...    |
                     |       +---------------------------+
                     |        (objective vs subjective)
                     +-------> aynı/ayrı cred object'e bakabilir

cred'i sıfırlamak subjective context'i root yapar; bazı check'ler real_cred ile tutarlılık beklediğinden ikisini de ele almak gerekebilir.

Walkthrough

  1. current / task_struct'ı bul — per-CPU current_task'ı oku, leak edilmiş bir kernel object'ten dolaş ya da task'ının marker'ı için kernel heap'ini tara.
  2. task_struct'tan task->cred'i (ve genellikle ayrıca real_cred'i) oku. Yaygın bir heap-scan trick'i: setresuid üzerinden sekiz id'nin hepsini bir sentinel'e ayarlayarak process'ini benzersiz bir cred ile etiketle, sonra canlı cred object'ini bulmak için slab'i o pattern için tara.
  3. uid,gid,suid,sgid,euid,egid,fsuid,fsgid'in 32 byte'ını AAW kullanarak sıfırla overwrite et. usage'a dokunma (refcount uid'den önce yaşar).

Beklenen: getuid() aynı process'te hemen 0 döner; bir root shell başlat.

Warning

struct cred, __randomize_layout ile annote edilmiştir, dolayısıyla CONFIG_RANDSTRUCT ile build edilmiş kernel'lerde field offset'leri build time'da randomize edilir ve hardcode edilemez — bunları türetmen ya da commit_creds'e geri dönmen gerekir. real_cred'i de handle etmeden cred'i overwrite etmek, bazı check'lerin fark ettiği tutarsızlıklar bırakabilir.

Mitigation

CONFIG_RANDSTRUCT cred field layout'unu randomize eder. Hardened allocator'lar ve credential object'lerinin pointer-authentication'ı (araştırma aşamasında) forge edilmiş/overwrite edilmiş cred'leri önlemeyi amaçlar. Data-only doğası bunu runtime'da tespit etmeyi zorlaştırır.

References