Skip to content

Kernel CFG (kCFG) bypass via data-only targets

Ring0 indirect call'ları doğrulayan kCFG, forward-edge'i korur; ama attacker forged bir indirect call yapmak yerine data pointer'ları, CFG'nin kapsamadığı function-pointer table'ları veya valid-but-abusable bir target'ı kötüye kullandığında bitmap hiç devreye girmez.

Mechanism

kCFG, Windows kernel'in forward-edge Control Flow Integrity mekanizmasıdır. Compiler her indirect call'un önüne bir check enjekte eder ve bu check __guard_dispatch_icall_fptr üzerinden çalışır: çağrılacak function pointer, valid call target'ların tutulduğu bir bitmap'e karşı doğrulanır. Hedef address bitmap'te registered bir kernel function entry'si değilse, KERNEL_SECURITY_CHECK_FAILURE (bugcheck 0x139) tetiklenir. Amaç, CWE-822 (Untrusted Pointer Dereference) tarzı bir corrupted callback pointer'ın attacker code'una redirect edilmesini engellemektir.

Invariant ve neden data-only onu delip geçer

kCFG yalnızca forward edge'i (bir function pointer üzerinden indirect call) doğrular. Bir dizi şeyi hiç kapsamaz ve data-only exploitation tam olarak bu boşlukları hedefler:

  • Backward edge yok: ret ile geri dönen control-flow transfer'ları kCFG denetlemez. Kernel stack'teki bir return address'i arbitrary write ile bozmak (ROP), bitmap'e hiç dokunmadan control'ü alır.
  • Pure data corruption: _KTHREAD.PreviousMode, token privilege bitmap'i, page table entry'leri veya I/O ring register buffer'ları gibi data'yı flip etmek hiçbir indirect call gerektirmez — dolayısıyla doğrulanacak bir target da yoktur.
  • CFG'nin kapsamadığı function-pointer table'lar: nt!HalDispatchTable gibi bazı dispatch table entry'leri veya third-party driver'ların .data section'ındaki callback pointer'ları, bitmap tarafından enforce edilmez; oraya valid bir kernel function bile yazmak check'i pass eder.
  • Valid-but-abusable target: Bitmap "bu bir meşru function entry mi?" sorusuna cevap verir, "bu çağrı bu context'te mantıklı mı?" sorusuna değil. Legitimate bir kernel function'ı unintended argument'larla çağırmak bitmap'i geçer.

VBS bağımlılığı

kCFG'nin tam gücü Virtualization-Based Security (VBS/HVCI) ister. HVCI, SLAT/EPT üzerinden bitmap page'ini immutable (read-only) yapar, böylece attacker bitmap'in kendisini rewrite edip malicious target'ı whitelist edemez. VBS disabled iken bitmap'ler pratikte NULL/etkisizdir ve check trivially geçilir. Ama HVCI enabled olsa bile yukarıdaki data-only yollar açık kalır — çünkü onlar bitmap'e saldırmaz.

Walkthrough

Aşağıdaki high-level akış, Connor McGarr'ın public "No Code Execution? No Problem" writeup'ındaki mantığı özetler: HVCI + kCFG enabled bir sistemde, forged indirect call yerine bir backward-edge / data-only yol seçilir.

  1. Attacker zaten bir kernel arbitrary write primitive'ine sahip (bir vulnerable driver veya bug). Hedef, code execution / privilege'i kCFG'yi flip etmeden elde etmek.

  2. Bitmap'e saldırmayı bırak. kCFG bitmap'ini overwrite etmeye çalışmak HVCI altında EPT tarafından access violation ile reddedilir; bu yol kapalı sayılır.

  3. Bir non-CFG-covered hedef seç. İki tipik seçenek:

  4. Backward edge / ROP: Kernel-mode stack'te bir known return address'i (writeup'ta nt!KiApcInterrupt civarı bir frame örneklenir) arbitrary write ile bir ROP chain'e yönlendir. ret doğrulanmadığı için chain, her biri "valid" bir dönüş noktası olan gadget'lardan geçerek örneğin nt!PsGetCurrentProcess gibi legitimate function'ları çağırır — completely valid function, completely invalid means.
  5. Pure data-only: Hiç control-flow hijack etme; PreviousMode benzeri bir data field'ı ya da token privilege bitmap'ini doğrudan flip et (bkz. see_also).
// conceptual, not a working chain
kcfg_check(fptr)  ->  forward-edge only; ret / data writes are out of scope
  attacker path A: overwrite kernel-stack return addr  -> ROP -> valid funcs
  attacker path B: overwrite data (PreviousMode / token / PTE) -> no call at all
  1. kCFG hiç trip etmez, çünkü ne bir invalid function pointer indirect call'ı yapıldı ne de bitmap'e yazıldı. Mitigation kendi threat model'i (forged indirect call) dışında kaldı.
Neden bu 'bypass' değil de 'sidestep'

kCFG teknik olarak kırılmadı; sadece kapsamadığı bir düzleme (backward edge + data) geçildi. Bu yüzden defense-in-depth, kCFG'yi kCET (shadow stack, backward-edge koruması) ve data-pointer hardening ile birlikte ister.

Detection

  • Bugcheck sinyali: Başarısız/yanlış-kalibre edilmiş forged indirect call denemeleri KERNEL_SECURITY_CHECK_FAILURE (0x139) bugcheck'i üretir; kernel-mode crash telemetry'sinde 0x139 artışı bir kCFG violation denemesine işaret eder.
  • Primitive-öncesi surface: Data-only yollar sessizdir, o yüzden EDR odağını ilk kernel write primitive'ine kaydır — genelde bir vulnerable/BYOVD driver load'u. Microsoft vulnerable-driver blocklist ihlalleri, beklenmeyen \Driver\ object'leri ve şüpheli NtLoadDriver / service-create event'leri primary indicator'lardır.
  • Anomali: Kendi _EPROCESS/token'ına kernel write yapıp ardından SeDebugPrivilege kazanan ya da SYSTEM token'ına geçen bir low-integrity process, downstream data-only escalation'ın imzasıdır.

Mitigation

  • VBS + HVCI enable et. kCFG'nin bitmap'ini immutable yapan ve bitmap-rewrite yolunu kapatan tek şey budur; VBS olmadan kCFG effectively no-op'tur.
  • kCET / Hardware-enforced Stack Protection enable et. Backward-edge (ROP) yolu, kCFG'nin body-eşdeğeri değil kCET/shadow stack tarafından kapatılır; ikisi birlikte forward + backward edge'i kaplar.
  • Attack-surface reduction. Microsoft vulnerable-driver blocklist, WDAC/App Control ve driver signing, data-only'nin varsaydığı ilk arbitrary write primitive'ini reddederek tüm zinciri kırar.
  • Pointer/data hardening. PreviousMode gibi tarihsel data-only primitive'lerin (24H2'de sertleştirildi) ve dispatch-table pointer'larının sürekli hardening'i, CFG'nin dışında kalan data düzlemini daraltır.

References