/proc/kallsyms symbol address leak¶
/proc/kallsyms'ten kernel symbol'lerinin runtime adreslerini okuyarak boot başına KASLR slide'ını kurtar vecommit_credsgibi 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:
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 (%peşdeğeri); ancak/proc/kallsymskallsyms_show_value()predicate'ini uygular — non-root okuyucu0görür, root /CAP_SYSLOGsahibi ise gerçek adresleri görür.1: okuyucudaCAP_SYSLOGve effective uid/gid == real uid/gid yoksa%pKpointer'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.