dmesg / kernel log buffer pointer leak¶
Read raw kernel addresses out of the printk ring buffer — oops/WARN/BUG register dumps print true (unhashed) kernel pointers, defeating KASLR.
Mechanism¶
Note
Kernel tüm log mesajlarını dairesel bir printk ring buffer'da tutar; bu, userspace'e
dmesg(8), /dev/kmsg, syslog-daemon dosyaları (/var/log/syslog, /var/log/kern.log)
ve syslog(2)/klogctl syscall'ı üzerinden açılır — hepsi aynı buffer'ı okur. Kernel
bir fault'a, WARN()'a, BUG()'a ya da bir oops'a çarptığında register durumunu, bir
call trace'i ve fault'lu instruction pointer'ı (RIP) döker. Linux 4.15'ten beri düz
%p specifier'ı hash'lenir, ama oops register dump'ları ve RIP: satırı ham hex
register değerleri olarak, %p üzerinden değil yayımlanır, dolayısıyla hash'lemeyi
bypass eder ve gerçek kernel sanal adreslerini açığa çıkarır. Leak'lenen bir .text
adresinden bilinen bir sembol offset'ini çıkararak attacker KASLR slide'ını hesaplar.
syslog(2) action'ları: SYSLOG_ACTION_READ_ALL = 3 (kalan her şeyi oku), READ_CLEAR = 4,
SIZE_BUFFER = 10. Man page'e göre, 3 ve 10 dışındaki tüm komutlar privilege gerektirir, ve
2.6.37'den beri 3/10 bile unprivileged process'lere yalnızca kernel.dmesg_restrict 0
olduğunda izin verilir. İlgili capability CAP_SYSLOG'dur.
Walkthrough¶
dmesg # syslog(2) / /dev/kmsg; respects dmesg_restrict
cat /dev/kmsg # raw ring-buffer device
cat /var/log/syslog # persisted copy from the syslog daemon
dmesg | grep -E 'RIP:|ffffffff[0-9a-f]{8}' # pull kernel addresses
Doğrudan syscall üzerinden (SYSLOG_ACTION_READ_ALL):
#include <sys/klog.h>
char buf[1<<16];
/* int klogctl(int type, char *bufp, int len); */
int n = klogctl(3 /* SYSLOG_ACTION_READ_ALL */, buf, sizeof(buf));
Kernel bug-hunting dokümanlarından gerçek bir WARN trace'i, adres leak'leyen sembol+offset ve instruction pointer'ı gösterir:
WARNING: CPU: 1 PID: 28102 at kernel/module.c:1108 module_put+0x57/0x70
...
[<c109e8a7>] ? module_put+0x57/0x70
BUG: unable to handle kernel NULL pointer dereference at (null)
IP: [<c06969d4>] iret_exc+0x7d0/0xa59
x86_64'te modern biçim, tam ffffffff........ adresleriyle RIP: 0010:<symbol>+offset/len
şeklindedir ve register dump'ı (RSP/RAX/.../R15) ham stack/heap/text pointer'ları taşır.
Warning
Bazı driver'lar oops olmadan bile adres leak'ler. CVE-2018-7273'te floppy
driver'ının drivers/block/floppy.c içindeki show_floppy'si kernel fonksiyonlarının
ve global'lerinin adreslerini doğrudan dmesg'e basıyordu; bu, KASLR'ı etkisizleştirmek
için herhangi bir kullanıcı tarafından okunabiliyordu. (Kesin printk format string'i
advisory'den kurtarılamadı.)
Detection¶
Log'daki oops/WARN sıçramaları, tekrarlanan syslog(2) READ_ALL/READ_CLEAR çağrıları
ya da hayatta kalan bir leak'i toplamak için kontrollü fault'lar tetikleyen bir process
gözlemlenebilir sinyallerdir.
Mitigation¶
sysctl -w kernel.dmesg_restrict=1(CONFIG_SECURITY_DMESG_RESTRICT) — buffer'ı okumak içinCAP_SYSLOGgerektirir.kptr_restrict=2— privilege'tan bağımsız olarak%pKpointer'larını sıfırlar.panic_on_oops=1— tekrarlanabilir, hayatta kalan bir oops-leak primitive'ini engeller.