Skip to content

kptr_restrict / %pK pointer leak bypass

%pK format specifier'ının credential- ve context-duyarlı logic'ini suistimal et, böylece kptr_restrict=1 altında yazdırılan bir kernel pointer sana sıfırlanmak yerine açık olarak gösterilsin.

Mechanism

Kernel pointer'ları vsnprintf üzerinden yazdırır. Leak'ler için iki specifier önemli: 4.15'ten beri hashed (her boot'ta randomize edilmiş) bir pointer yazdıran %p ve gerçek adresi yalnızca uygun şekilde yetkili reader'lara göstermesi gereken %pK ("restricted pointer"). %pK için kapı, lib/vsprintf.c:restricted_pointer()'da değerlendirilen kptr_restrict sysctl'idir.

Note

Kernel'in zorlamaya çalıştığı invariant şudur: "gerçek bir kernel pointer userspace'e yalnızca reader CAP_SYSLOG tutuyorsa ve başlatıldığı aynı uid/gid ile çalışıyorsa ulaşır." Klasik bypass, bu check'in open time'da değil read/print time'da ve yalnızca kptr_restrict=1'de yapıldığı gerçeğini suistimal eder. İlgili logic:

switch (kptr_restrict) {
case 0:
    /* Handle as %p, hash and do _not_ leak addresses. */
    return default_pointer(buf, end, ptr, spec);
case 1: {
    const struct cred *cred;
    /* kptr_restrict==1 cannot be used in IRQ context ... */
    if (in_hardirq() || in_serving_softirq() || in_nmi()) {
        ...
        return error_string(buf, end, "pK-error", spec);
    }
    cred = current_cred();
    if (!has_capability_noaudit(current, CAP_SYSLOG) ||
        !uid_eq(cred->euid, cred->uid) ||
        !gid_eq(cred->egid, cred->gid))
        ptr = NULL;
    break;
}
case 2:
default:
    /* Always print 0's for %pK */
    ptr = NULL;
    break;
}

Yani bypass aslında bir code defect'i değil, bir configuration/operasyonel hatadır:

  • kptr_restrict=0 (kernel default'u), %pK'yı hiç restrict etmez — %p hashing'e düşer ve pointer hashing'den önce gelen herhangi bir %pK site'ı ya da herhangi bir düz %lx/%px print'i gerçek bir adresi leak eder.
  • kptr_restrict=1, pointer'ı yalnızca reader CAP_SYSLOG'tan yoksunsa ya da uyumsuz real/effective id'lere sahipse sıfırlar. Privileged, non-setuid bir reader yine de gerçek değeri alır. Tree içindeki yorum, check'in neden read time'da olduğunu açıkça belirtir: bir binary bir %pK dosyasını open() edip ardından read()'ten önce bir setuid wrapper'a düşebilir, dolayısıyla kernel credential'ları print time'da yeniden kontrol eder.
  • kptr_restrict=2, %pK'yı koşulsuz sıfırlayan tek değerdir.

Önemli olarak, %pK birçok specifier arasında biridir. Birçok leak sink'i (/proc/kallsyms, dmesg, /sys/kernel/..., slab debug dosyaları, /proc/*/stack) başka toggle'lar (dmesg_restrict, perf_event_paranoid, /proc/sys/kernel/kallsyms perm'leri) tarafından yönetilir — dolayısıyla "%pK altında adres 0000000000000000" demek, adresin başka bir path'ten ulaşılamaz olduğu anlamına gelmez.

Walkthrough

Mevcut policy'yi incele ve değerler arasındaki farkı göster.

$ cat /proc/sys/kernel/kptr_restrict
1
$ id
uid=1000(user) gid=1000(user) groups=1000(user)

/proc/kallsyms gibi bir %pK sink'i (symbol adreslerini %pK ile yazdırır), kptr_restrict=1 altında unprivileged bir reader için adresleri sıfırlar:

$ head -n 3 /proc/kallsyms
0000000000000000 T startup_64
0000000000000000 T secondary_startup_64
0000000000000000 T __pfx_secondary_startup_64

Aynı dosya, eşleşen real/effective id'lerle CAP_SYSLOG tutan bir process tarafından okunduğunda gerçek adresleri döndürür, çünkü has_capability_noaudit(current, CAP_SYSLOG) geçer ve uid_eq(euid,uid) sağlanır:

# capsh --caps="cap_syslog+eip" -- -c 'head -n 1 /proc/kallsyms'
ffffffff81000000 T startup_64

Warning

CAP_SYSLOG tutmak ama ona bir setuid program yoluyla düşmüş olmak leak etmez: !uid_eq(cred->euid, cred->uid) clause'u effective ve real id'ler farklı olduğunda tetiklenir, dolayısıyla pointer NULL'a zorlanır. Bu "open-then-elevate" savunmasıdır ve aynı zamanda dikkatsiz bir kptr_restrict=1 deployment'ının basitçe CAP_SYSLOG'a doğrudan sahip olan (örn. birçok container/service account) yerel bir process'e karşı gerçek bir koruma olmamasının nedenidir.

En yaygın pratik "bypass", makinenin upstream default'u 0'da bırakılmasıdır:

$ cat /proc/sys/kernel/kptr_restrict
0
$ grep ' commit_creds$' /proc/kallsyms
ffffffff810c8e40 T commit_creds

Burada %pK path'i %p hashing'e devreder, ama /proc/kallsyms da kendi permission check'ine tabidir; eğer kptr_restrict=0 ise ve dosya okunabilirse, gerçek adresler doğrudan çıkar. Bu tek leak edilen symbol KASLR'ı yener: kernel base'i kurtarmak için leak edilen değerden commit_creds'in bilinen offset'ini çıkar (proc-kallsyms-symbol-address-leak'a bak).

Yalnızca leak'in (data'nın değil) ayrıcalığını yükseltmek için şunu ayarla:

# sysctl -w kernel.kptr_restrict=2
# sysctl -w kernel.dmesg_restrict=1

Warning

%pK yalnızca %pK kullanan site'ları kapsar. Pointer leak'leri sıklıkla hâlâ %lx/%px ile yazdıran code'dan, dmesg'den (dmesg_restrict ile kapılı, dmesg-kernel-log-buffer-pointer-leak'a bak) ya da uninitialized-memory infoleak'lerinden gelir. kptr_restrict=2 ayarlamak %pK kanalını kapatır ama tek başına genel bir anti-KASLR önlemi değildir.

Detection

  • Hardening baseline'larında kptr_restrict ve dmesg_restrict'in her ikisinin de sıfır olmadığını denetle; kptr_restrict=0 upstream default'tur ve işaretlenmelidir.
  • /proc/kallsyms, /proc/<pid>/stack ve slab debug dosyalarını okuyan unprivileged process'leri izle; bir non-CAP_SYSLOG task'a tüm-sıfır olmayan bir değer döndürülmesi bir misconfiguration ya da non-%pK bir leak path'ine işaret eder.
  • restricted_pointer() path'i, %pK hard IRQ / softirq / NMI context'inde (yanlışlıkla) kullanıldığında pK-error yayar — loglarda görünmesi buggy bir print site'ını işaretler.

Mitigation

  • kernel.kptr_restrict=2 ve kernel.dmesg_restrict=1 ayarla; %pK açan dosyalardan world-read'i kaldır.
  • CAP_SYSLOG'u hassas bir capability olarak ele al: bunu bir service account'a vermek, kptr_restrict=1 altında gerçek-pointer okumalarını fiilen yeniden etkinleştirir.
  • Yeni code için %pK yerine %p'nin per-boot pointer hashing'ine (4.15'ten beri) güven ve ulaşılabilir sink'lerde adresleri asla %px/%lx ile yazdırma. KASLR ile birleştir, böylece tek bir leak edilen adres tüm image'i tetikçe map'lemesin.

References