Stack Pivot into physmap/direct-map¶
ret2dir tehdit modeli (savunma notu). Kontrollü bir payload userspace'e spray'lendiğinde kernel'in direct map'inde (physmap) bir kernel-resident alias kazanır; saldırgan kernel stack pointer'ını o alias'a pivot ederek SMEP/SMAP'i baypas edebilir. Bu not, aşağıdaki
## Mitigationbölümündeki savunmaların (özellikle XPFO ve physmap KASLR) hedeflediği tehdidi belgeler.
Mechanism¶
Aşağıdaki mekanizma weaponize edilmiş bir tarif değildir; ret2dir'in physmap aliasing invariant'ını, ## Mitigation bölümündeki savunmaların neden gerektiğini göstermek için konsept düzeyinde açıklar.
Note
Linux kernel'i, sabit bir kernel-virtual base'de tüm physical RAM'in linear
("direct") bir mapping'ini tutar — physmap. x86-64'te runtime base'i
page_offset_base symbol'üdür (compile-time constant __PAGE_OFFSET, modern
kernel'lerde 0xffff888000000000; 4.20-öncesi kernel'lerde 0xffff880000000000).
4-seviyeli (48-bit) paging ile bölge ffff888000000000–ffffc87fffffffff (64 TB)
aralığını kapsar; 5-seviyeli (57-bit) paging ile 32 PB'a yayılır.
Anahtar invariant: userspace'e verilen her physical frame, aynı anda physmap üzerinden kernel'e map'lenir. Dolayısıyla bir user page'in bir kernel synonym'u vardır — aynı physical byte'ları alias'layan bir direct-map virtual address.
Bu, ret2usr savunmalarının ardındaki varsayımı kırar. SMEP/SMAP (ve ARM PXN/PAN,
PaX KERNEXEC/UDEREF) kernel'in user page'leri execute etmesini ya da okumasını
durdurur. Ama physmap synonym'u üzerinden referans verilen bir payload bir
kernel address'idir — CPU bir kernel page görür, dolayısıyla SMEP/SMAP
ateşlenmez. ret2dir ("Rethinking Kernel Isolation," Kemerlis, Polychronakis,
Keromytis, USENIX Security 2014) bu tekniği adlandırır: bir user page'e
ret2usr yapmak yerine, exploit control'ü ya da bir stack pivot'u attacker-controlled
data'nın kernel-resident synonym'una yönlendirir.
Saldırıyı bir stack pivot tamamlar. Control flow'u hijack ettikten sonra,
RSP'yi kontrollü bir register'dan load eden bir gadget — xchg rsp, rXX ; ret,
mov rsp, ... ya da leave ; ret — kernel stack'ini, saldırganın spray'leyerek
öngörülebilir bir physmap address'ine yerleştirdiği bir fake stack / ROP chain'e
taşır. Chain direct map'te yaşadığı için, ROP yürütmesi sırasında hiç user-page
erişimi olmaz.
Bir user address'inin synonym'u, onun physical frame number'ından hesaplanabilir:
PFN(uaddr) = pagemap[(uaddr/4096) * 8][0:54]
SYN(uaddr) = page_offset_base + 4096 * (PFN(uaddr) - PFN_MIN)
KASLR kernel'lerinde (CONFIG_RANDOMIZE_MEMORY / CONFIG_RANDOMIZE_BASE)
physmap base boot'ta randomize edilir, dolayısıyla pivot etmeden önce
page_offset_base'in bir info leak'i gerekir.
Walkthrough¶
Adım 1 — physmap spray: birçok anonymous page allocate edip her birine ROP chain'i yazarak physical RAM'i payload kopyalarıyla kapla:
void *physmap_spray[16 * 1024]; /* ~64 MB of copies */
for (int i = 0; i < 16 * 1024; i++) {
void *mp = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(mp, payload, 0x500); /* fake stack / ROP chain */
physmap_spray[i] = mp;
}
Adım 2 — bir direct-map target türet. Tarihsel olarak /proc/<pid>/pagemap
PFN'i doğrudan veriyordu (yukarıdaki SYN()'i uygula). Spray geniş bir bölgeyi
kapladığından, bunun yerine onun içine düşen page-aligned bir physmap address'i
hedefleyebilirsin:
/* guess a synonym address within the sprayed region */
unsigned long physmap_ptr = (g_buf & ~0xfffUL) + 16 * 1024 * 1024;
/* e.g. 0xffff888001a39000 — not perfectly stable run-to-run, hence the wide spray */
Adım 3 — pivot. Saved kernel RIP'i (örn. corrupt edilmiş bir function pointer ya da overflow'a uğramış bir kernel object üzerinden) bir pivot gadget'ıyla overwrite et ki RSP chain'e insin:
Lexfo CVE-2017-11176 writeup'ı tam da o aliasing tuhaflığını exploit eder:
xchg eax, esp üst 32 bit'i sıfırladığı için, ULAND_WQ_ADDR = FAKE_STACK_ADDR
+ 0x100000000 ayarlar ki truncate edilmiş RSP yine de kontrollü stack'i
göstersin. Pivot'tan sonra, physmap'teki ROP chain kernel context'inde execute olur.
Beklenen sonuç: control, kernel-space (direct-map) bir address'teki spray'lenmiş
ROP chain'e aktarılır ve hiçbir user page'e dokunulmadığı için SMEP/SMAP asla
tetiklenmez. Modern uyarı: /proc/<pid>/pagemap artık world-readable değil; bu da
kolay PFN→synonym leak'ini ortadan kaldırır, dolayısıyla spray-edip-tahmin-etme
(ya da ayrı bir physmap-address leak'i) gerekir.
Detection¶
- Bir indirect call/return sonrasında RSP'nin aniden direct-map aralığına
(
0xffff8880…+) işaret ettiği bir kernel oops, bir physmap pivot'unun ayırt edici özelliğidir. - Bir crash'ten hemen önce büyük anonymous
mmapallocation patlamaları, bir physmap spray'ine işaret eder.
Mitigation¶
- XPFO (eXclusive Page Frame Ownership) — ret2dir makalesinin hedefli savunması
(Haefliger, Stecklina, Aziz, Andersen). Userspace'e gidecek page'ler allocation'da
physmap'ten unmap edilir ve reclaim'de yeniden map'lenir (ve sıfırlanır), böylece
synonym fault eder. Bkz. exclusive-page-frame-ownership.
Naif stale-TLB flushing onu çok pahalı yapmıştı (96 core'da
make -jsystem time'ı 26.37×'e kadar); lazy per-CPU deferred + batched flush'lar worst-case overhead'i ~1.27–1.28×'e indirdi. XPFO yalnızca x86-64 / 4K-page'dir ve mainline'a merge edilmemiştir. - SMAP/SMEP (smap-bypass-via-kernel-resident-payload) ret2usr'ı engeller ama tek başlarına ret2dir'i engellemez — payload bir kernel page'dir.
- physmap base'inin KASLR'si (
CONFIG_RANDOMIZE_MEMORY) birpage_offset_baseleak'ini zorunlu kılarak çıtayı yükseltir ama tek başına pivot'u önlemez. Bkz. kernel-address-space-layout-randomization.
References¶
- Exclusive page-frame ownership (LWN.net)
- Add support for eXclusive Page Frame Ownership (XPFO) (LWN.net)
- XPFO v6 — lazy TLB flush performance (LWN.net)
- x86-64 memory map (Documentation/x86/x86_64/mm.txt, kernel.org)
- Kernel Exploitation Techniques: Turning The (Page) Tables (sam4k)
- Linux Kernel PWN | 05 ret2dir (Fernweh)
- CVE-2017-11176: A step-by-step Linux Kernel exploitation, part 4/4 (Lexfo)