Skip to content

KASLR bruteforce

Kernel base'i düşük entropy'ye sahip olduğunda (x86-64'te ≈8–9 bit) ve başarısız bir tahmin sistemi crash etmediğinde, bir exploit'i her aday base'e karşı tekrar tekrar denemek — veya ortaya çıkan oops'tan sağ çıkmak — KASLR'ı bir information leak yerine tüketme yoluyla yener.

Mechanism

Note

Kernel ASLR, kernel image'inin fiziksel/virtual load adresini boot'ta bir kez randomize eder. x86-64'te image, üstteki 1 GiB penceresinde 0xffffffff800000000xffffffffc0000000, 2 MiB-hizalı olarak map'lenir — yani yalnızca 512 olası slot vardır (her 2 MiB için bir tane), yani ~9 bit entropy (LWN'in erken patch'i "e820 map'e bağlı olarak en iyi ihtimalle sekiz bit" demişti). KASLR'ın güvenliği, attacker'ın o birkaç yüz slot'tan hangisinin canlı olduğunu bilmemesine dayanır. Brute force tam olarak o varsayıma saldırır: bu kadar küçük bir arama uzayıyla her slot'u denemek hesaplama açısından önemsizdir — yoldaki tek şey yanlış bir tahminin maliyetidir.

Yanlış bir tahmin, amaçlanan kernel object'i olmayan bir adresi dereference eder. Bunun sağ kalınabilir olup olmaması brute force'un işe yarayıp yaramayacağını belirler:

  • Crash-on-miss (varsayılan hardened): "İlgili adresi bulmaya yönelik bir brute force girişimi muhtemelen bir kernel oops'uyla sonuçlanır; bu, hatanın doğasına ve panic_on_oops sysctl'inin değerine bağlı olarak bir panic'e de yol açabilir." panic_on_oops=1 ile (veya kurtarılamaz state'i bozan herhangi bir miss ile) makine yeniden başlar — KASLR yeniden randomize eder — ve brute force "yüksek kernel panic oranı nedeniyle neredeyse uygulanabilir değildir."
  • Survivable-miss (exploit edilebilir durum): Yanlış bir tahmin yalnızca current task'ı öldürürse (panic yapmayan bir oops), attacker basitçe bir sonraki adaya karşı fork()'lar/yeniden dener. ≤512 slot ile ortalama ~256 deneme base'i bulur. Bu, CTF kernel'larında (panic_on_oops=0) ve faulting path'in process başına temiz şekilde unwind ettiği gerçek bug'larda yaygındır.

Yani saldırılan invariant düşük entropy + sağ kalınabilir hatadır. İkisinden birini kaldır (daha fazla entropy veya miss'te ölümcül) ve brute force çöker.

Walkthrough

512 aday base'i numaralandır ve biri fault etmeyene kadar her birini probe et.

#include <stdint.h>
#define KBASE_MIN  0xffffffff80000000UL
#define KBASE_MAX  0xffffffffc0000000UL
#define KBASE_ALIGN 0x200000UL              /* 2 MiB */
#define SLOTS  ((KBASE_MAX - KBASE_MIN) / KBASE_ALIGN)   /* 512 */

/* try_base() runs the exploit assuming this kernel base.
   Returns 0 if the guess survived (likely correct), nonzero on miss. */
for (uint64_t i = 0; i < SLOTS; i++) {
    uint64_t base = KBASE_MIN + i * KBASE_ALIGN;
    pid_t pid = fork();
    if (pid == 0) {
        _exit(try_base(base));            /* child absorbs the oops/SIGKILL */
    }
    int st; waitpid(pid, &st, 0);
    if (WIFEXITED(st) && WEXITSTATUS(st) == 0) {
        printf("[+] kernel base = %#lx\n", base);
        break;
    }
}

Her deneme için fork yapmak kilit numaradır: sağ kalınabilir bir oops yalnızca child'ı öldürür, parent bir sonraki slot'a geçer. Bir hedefte arama uzayını doğrula:

# Real base (root only) — used to validate a brute-forcer offline:
$ sudo grep ' _text' /proc/kallsyms
ffffffff9b000000 T _text          # observe it is 2 MiB-aligned within the window
Neden milyonlarca değil ~512 deneme

window      = 0xffffffffc0000000 - 0xffffffff80000000 = 0x40000000 (1 GiB)
alignment   = 0x200000 (2 MiB)
slots       = 0x40000000 / 0x200000 = 512
entropy     = log2(512) = 9 bits
expected tries to hit  = 512 / 2 = 256
Aynı yaklaşımın umutsuz olduğu userspace ASLR (genellikle 28+ bit) ile karşılaştır.

Warning

Brute force gürültülü ve kırılgandır. panic_on_oops=1 ise (birçok dağıtımda production varsayılanı ve Android/CrOS normu) tek bir miss kutuyu yeniden başlatır ve yeniden randomize eder — araman her boot'ta yeni entropy ile sıfırdan başlar, başarıyı fiilen imkânsız kılar. Ayrıca bir oops/dmesg entry'leri izi bırakır. Bir gerçek info leak varsa onu tercih et; brute force'u düşük-entropy'li, oops'tan sağ kalınabilir hedefler için sakla (birçok CTF image'i, panic_on_oops=0). Not: KASLR brute force ve side channel KASLR kırma (prefetch timing, EntryBleed) farklı tekniklerdir — ikincisi base'i tahmin-edip-fault-etmeden okur.

Detection

  • Aynı UID'den sıkı bir döngüde dmesg'de tekrarlanan oops/general protection fault/BUG: entry'leri güçlü bir sinyaldir.
  • Her biri artan adreslere karşı aynı syscall'ı çıkaran yüzlerce kısa-ömürlü child'ı fork()'layan bir process EDR / audit için anormaldir.
  • Boot-time panic_on_oops ve oops-oranı izleme (LWN makalesi "sistem izleme tekrarlanan oops'ları da yakalayabilir" der).

Mitigation

  • panic_on_oops=1 (ve oops=panic) set et ki her miss ölümcül olsun — ucuz survivable-miss'i yeniden randomize eden bir reboot'a çevirir.
  • CONFIG_PANIC_ON_OOPS / hardened config'ler; tekrarlanan oops'ları tetikleyen process'leri rate-limit'le veya öldür.
  • Entropy'yi artır: FGKASLR / function-granular randomization ve daha büyük KASLR pencereleri per-slot isabet olasılığını küçültür.
  • Yapısal fix, sağ kalınabilir yeniden denemeleri reddetmektir — oops'tan sağ kalınabilir bir miss olmadan, düşük entropy tek başına brute force için yeterli değildir.

References