Windows Control Flow Guard¶
Her indirect call'u önceden hesaplanmış geçerli function-entry target kümesine kısıtlayan compiler + OS forward-edge control-flow integrity'si; böylece corrupt olmuş bir function pointer yalnızca yasal bir entry point'e ulaşabilir ya da process'i terminate eder.
Mechanism¶
Neden çalışır
Klasik bir exploit bir function pointer'ını (ya da C++ vtable entry'sini)
corrupt eder ki bir indirect call/jmp attacker-chosen koda düşsün. Control
Flow Guard (CFG) bunu forward edge üzerinde bir invariant zorlayarak kırar:
bir indirect call yalnızca, build system'in zaten yasal bir indirect-call
target olduğunu kanıtladığı bir adrese transfer olabilir.
- Build zamanında, "kodundaki tüm indirect call'lar, kod doğru çalıştığında
ulaşabileceği her konumu bulmak için analiz edilir. Bu bilgi binary'lerinin
header'larındaki ekstra structure'larda saklanır." Linker bunları Guard CF
Function Table'da (the "FID table"), geçerli target entry point'lerinin bir
RVA list'inde toplar ve PE'yi
CF Instrumented/FID table presentolarak işaretler. - Compiler, "her indirect call'dan önce ... target'ın doğrulanmış konumlardan
biri olduğundan emin olan bir check inject eder." Check,
__guard_check_icall_fptrüzerinden çağırır. CFG'den habersiz bir binary'de bu bir no-op stub'a işaret eder (backward compatibility'yi korur); CFG-aware bir OS'ta loader onuntdll!LdrpValidateUserCallTarget'a yeniden yazar. - Validator, kernel tarafından bakımı yapılan ve "geçerli indirect call
target'larını tanımlayan state'i verimli biçimde tutan" bir bitmap'e
başvurur. Her bit, address space'in align edilmiş bir dilimine karşılık gelir;
bit'i clear olan bir target reddedilir.
VirtualAlloc/VirtualProtect, executable committed page'leri default olarak geçerli işaretler veSetProcessValidCallTargetsbir JIT'in target'ları dinamik yönetmesine izin verir. - "Runtime'da bir CFG check başarısız olduğunda, Windows programı derhal
terminate eder" — başarısız bir
LdrpValidateUserCallTargetbir fast-fail (STATUS_STACK_BUFFER_OVERRUN,0xC0000409) yükseltir.
Invariant: bir indirect call'un hedefi, saldırganın genişletemeyeceği, build-time, OS-protected bir function entry point whitelist'inden çekilir.
Walkthrough¶
1. CFG ile build et. Compiler'a /guard:cf, linker'a /GUARD:CF geç;
/DYNAMICBASE (ASLR) de gereklidir.
/guard:cf "compiler'ın compile zamanında indirect call target'ları için control
flow'u analiz etmesine ve target'ları doğrulamak için runtime'da kod eklemesine
neden olur. Default olarak /guard:cf kapalıdır ve açıkça enable edilmelidir."
2. Binary'nin opt-in olduğunu dumpbin ile doğrula:
Expected indicators
"CFG-enabled binary'lerin EXE ya da DLL characteristics list'indeGuard
bulunur ve Guard Flags CF Instrumented ile FID table present'ı içerir."
3. Eklenen check neye benzer. Her indirect call'dan önce compiler bir
__guard_check_icall_fptr load'u emit eder ve onu target rcx'te olacak şekilde
çağırır; OS routine'i o target için CFG bitmap'ini indeksler ve bit clear ise
fast-fail eder.
Coverage gap'leri whitelist'i zayıflatır
"CFG'yi tüm kod için enable edememek, korumada gap'ler açabilir." /guard:cf
ile build edilmemiş bir modül checked olmayan indirect call'lar yapar ve
PAGE_TARGETS_INVALID/PAGE_TARGETS_NO_UPDATE olarak oluşturulan JIT region'ları
target'larını kendileri register etmeli ya da erişilemez hâle gelmelidir.
Detection¶
dumpbin /loadconfig, Windows Defender Exploit Guard / process-mitigation policy
sorguları (ProcessControlFlowGuardPolicy) ve BinSkim, bir CFG process'ine
yüklenen non-CFG modülleri flag'leyebilir. Bir CFG ihlali bir 0xC0000409
fast-fail crash olarak ortaya çıkar; bu, WER/Defender Exploit Protection
event'lerinde FAIL_FAST_GUARD_ICALL_CHECK_FAILURE olarak da yüzeye çıkar. EDR
açısından sinyal, untrusted-input işlerken CFG-korumalı process'lerde erken
crash'ler ya da korunan process'lere karışık yüklenen non-CFG module'lerdir.
Mitigation¶
(Residual risk / nasıl bypass edilir.) CFG yalnızca bir forward-edge
mitigation'dır: return address'leri korumaz, dolayısıyla stack üzerindeki
saklanmış bir return address'i overwrite eden klasik ROP tamamen etkilenmez — o,
hardware-enforced stack protection'ın
(Intel CET shadow stack'leri) işidir. Forward edge'de bile whitelist kabadır:
FID table'daki herhangi bir function entry point'i yasal bir target'tır,
dolayısıyla bir saldırgan bir call'u farklı ama geçerli, hassas bir fonksiyona
yönlendirebilir. Diğer belgelenmiş zayıflıklar arasında bazı layout'larda yine de
bitmap-valid olan bir target üzerinden bir fonksiyonun ortasına çağrı yapmak,
suppressed/exported call target'larını suistimal etmek ve non-CFG modülleri yer
alır. Type-aware halefi
eXtended Flow Guard target kümesini sıkılaştırır
ve kernel CFG modeli ntoskrnl'e genişletir.
Kaynak yeniden derleme mümkün değilse
/guard:cf ile yeniden derleme mümkün olmadığında, CFG opt-in olmayan ama
uyumlu binary'ler için Exploit Protection (process-mitigation policy)
üzerinden zorlanabilir; korunan process'lere non-CFG module load etmekten yine
de kaçınılmalıdır.
References¶
- Microsoft Learn. Control Flow Guard for platform security. — https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard
- Microsoft Learn. Exploit protection reference. — https://learn.microsoft.com/en-us/defender-endpoint/exploit-protection-reference
- Microsoft Learn. /guard (Enable Control Flow Guard). — https://learn.microsoft.com/en-us/cpp/build/reference/guard-enable-control-flow-guard?view=msvc-170
- Microsoft Learn. /GUARD (Enable Guard Checks). — https://learn.microsoft.com/en-us/cpp/build/reference/guard-enable-guard-checks?view=msvc-170