Skip to content

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 ## Mitigation bö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 ffff888000000000ffffc87fffffffff (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:

0xffffffff8107b6b8 : xchg eax, esp ; ret   ; note: 32-bit xchg zeroes top 32 bits of RSP

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 mmap allocation 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 -j system 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) bir page_offset_base leak'ini zorunlu kılarak çıtayı yükseltir ama tek başına pivot'u önlemez. Bkz. kernel-address-space-layout-randomization.

References