Skip to content

/proc/kallsyms symbol address leak

/proc/kallsyms'ten kernel symbol'lerinin runtime adreslerini okuyarak boot başına KASLR slide'ını kurtar ve commit_creds gibi privesc gadget'larını bul.

Mechanism

Note

/proc/kallsyms her kernel symbol'ünü runtime virtual address'iyle birlikte dışa aktarır. KASLR, kernel text base'ini yalnızca boot'ta bir kez randomize eder, dolayısıyla tek bir sızdırılmış symbol adresi tüm slide'ı ifşa eder: slide = leaked_addr − known_base_address. Bir gerçek adresi bildiğinde her diğer symbol derandomize olur.

Adresler %pK format specifier'ı ile basılır; bu da kernel.kptr_restrict sysctl'i tarafından geçit altına alınır. Check, open() zamanında değil read() zamanında değerlendirilir, dolayısıyla open() ile read() arasındaki bir privilege değişimi adresi sızdırmaz. Altta yatan predicate kallsyms_show_value(cred)'tir — /proc/modules'un kullandığı aynı geçit.

Walkthrough

Klasik privesc gadget'larını bul:

$ grep prepare_kernel_cred /proc/kallsyms | head -1
ffffffff84056fec T prepare_kernel_cred
$ cat /proc/kallsyms | grep commit_creds

Satır formatı, boşlukla ayrılmış üç alandır: <address> <type> <name> (64-bit'te 16 hex hane; type bir nm tarzı karakterdir, örn. T = global text, r = read-only data).

kptr_restrict değerleri unprivileged bir okuyucu için sıfırladığında, aynı symbol tümü sıfır olarak geri okunur:

$ cat /proc/sys/kernel/kptr_restrict
1
$ grep prepare_kernel_cred /proc/kallsyms | head -1
0000000000000000 T prepare_kernel_cred

Symbol table (__ksymtab_*) girişlerini dışa aktaran sistemlerde gerçek adres, ksymtab girişinin adresi artı sakladığı relative offset'tir — örn. bir CTF kernel'inde KASLR-açık bir okuma:

$ cat /proc/kallsyms | grep ksymtab_commit_creds
ffffffffb7f87d90 r __ksymtab_commit_creds

Warning

Adresler sıfırlandığında bile kallsyms girişleri adrese göre sıralı kalır, bu da hâlâ relative symbol sıralamasını sızdırabilir. Leak yalnızca root olarak ya da bir setuid/helper context üzerinden okuyabiliyorsan işe yarar — unprivileged isen başka bir bug ile birleştir.

Detection

kallsyms_show_value() her okumada ulaşılır; denetim araçları, /proc/kallsyms'i okuyup sıfır olmayan değerler alan unprivileged process'leri izler — ki bu kptr_restrict=0 anlamına gelir.

Mitigation

kernel.kptr_restrict'i ayarla:

  • 0 (varsayılan): generic %pK çıktısı için pointer hash'lenir (%p eşdeğeri); ancak /proc/kallsyms kallsyms_show_value() predicate'ini uygular — non-root okuyucu 0 görür, root / CAP_SYSLOG sahibi ise gerçek adresleri görür.
  • 1: okuyucuda CAP_SYSLOG ve effective uid/gid == real uid/gid yoksa %pK pointer'ları sıfırlarla değiştirilir.
  • 2: pointer'lar privilege'dan bağımsız olarak sıfırlanır.

Ubuntu varsayılan olarak kernel.kptr_restrict=1 ile gelir. Benzer dmesg leak'ini kapatmak için kernel.dmesg_restrict=1 ile eşle.

References