SafeSEH¶
A compile/link-time Windows defense that embeds a read-only table of an image's legitimate exception-handler entry points so the OS can reject a corrupted SEH handler pointer before dispatching to it.
Mechanism¶
Note
32-bit x86 Windows'ta, bir thread'in structured-exception-handler (SEH) kayıtları stack üzerinde yaşar: her kayıt bir next pointer ve bir handler function pointer tutar. Bir stack buffer overflow bir kaydı overwrite edebilir ve sonra bir exception raise edildiğinde dispatcher zinciri walk eder ve saldırgan-kontrollü handler pointer'ını çağırır — cookie kontrol edilmeden önce tetiklenerek bir stack cookie'yi atlatan klasik SEH-overwrite kod yürütmesi.
SafeSEH bunu, linker'ın (/SAFESEH) image'deki her geçerli exception-handler entry
point'inin bir tablosunu üretmesini sağlayarak kapatır; bu, PE load configuration
directory'sine (SEHandlerTable / SEHandlerCount) kaydedilir. Dispatch sırasında OS
aday handler'ı bu tabloya karşı kontrol eder; kayıtlı bir handler olmayan bir pointer
reddedilir ve asla çağrılmaz. Sınır tutar çünkü meşru handler kümesi, saldırganın
runtime'da genişletemeyeceği read-only metadata'da build time'da sabittir.
SafeSEH yalnızca x86'dır: x64/ARM'da exception handler'lar stack-walk edilen pointer'lar
yerine table-based unwind data (PDATA/XDATA) ile tanımlanır, bu yüzden SEH-overwrite
sınıfı aynı şekilde geçerli olmaz.
Walkthrough¶
Kavramsal; Microsoft'un linker dokümanlarından ve MSRC'nin SEHOP yazısından.
- Flag ile build et.
/SAFESEH(ya da elle yazılmış MASM handler'ları için.SAFESEH) linker'a handler entry point'lerini toplamasını söyler:
Image sonra kavramsal olarak __safe_se_handler_table[] (base) ve
__safe_se_handler_count'a eşdeğer iki linker-üretimi sembol export eder.
-
Dispatch'te. Bir exception tetiklendiğinde, dispatcher handler pointer'ını stack üzerindeki kayıttan alır ve onun image'in safe-handler tablosundaki address'lerden biri olduğunu doğrular.
-
Saldırganın overwrite'ı başarısız olur. Overwrite edilmiş bir handler artık bir stack buffer'a, heap spray'e ya da arbitrary code'a işaret eder — kayıtlı bir handler'a değil — bu yüzden kontrol başarısız olur ve handler asla çağrılmaz.
Warning
Koruma yalnızca kapsadığı image kadar eksiksizdir. Linker mevcut bir binary'yi sonradan işaretleyemez; metadata build time'da eklenmelidir. Process'teki herhangi bir modül SafeSEH olmadan build edildiyse (legacy DLL, eski toolchain), onun handler'ları bir tabloda değildir ve enforcement politikasına bağlı olarak o modülün içine — ya da tüm image'lerin dışına — işaret eden bir handler hâlâ uygun bir hedef olabilir. Bu açık tam olarak SEHOP'un (runtime chain-integrity doğrulaması) tamamlayıcı, recompile gerektirmeyen bir savunma olarak getirilmesinin nedenidir.
Detection¶
SafeSEH'in kendisi enforcement bir handler'ı bloklayana kadar sessizdir; pratik sinyaller
posture audit ve crash telemetry'dir. /SAFESEH olmadan build edilmiş modülleri
bulmak için binary'leri safe-SEH tablosunun varlığı açısından denetleyin (PE load-config'in
sıfır olmayan bir SEHandlerCount'u vardır). Runtime'da, bir SEH-overwrite denemesi tipik
olarak exception-dispatch yolunda bir access-violation içeren bir process crash / Windows
Error Reporting olayı ya da corrupt edilmiş bir exception chain üzerinde bir EDR
exploit-guard uyarısı verir. Aksi halde hardened olan process'lerde tablodan yoksun
beklenmedik legacy 32-bit modülleri avlayın.
Mitigation¶
- Tüm 32-bit x86 modülleri için
/SAFESEHile compile/link edin ve MASM handler'larını kaydetmek için.SAFESEHkullanın;/SAFESEH:NO'dan kaçının. - Hassas process'lerden SafeSEH olmayan modülleri eleyin; karışık yüklemeler başlıca zayıflıktır.
- Ek savunmalar katmanlayın: modüllerin nasıl build edildiğinden bağımsız olarak handler chain'ini runtime'da doğrulamak için SEHOP'u etkinleştirin, stack cookie'leri ve DEP/ASLR'yi açık tutun ve table-based unwind modelinin overwrite sınıfını kaldırdığı 64-bit build'leri tercih edin.
- Bilinen bypass uyarıları: tarihsel bypass'lar tek bir SafeSEH olmayan modülü, herhangi bir image'in dışına yerleştirilmiş handler'ları ya da yolu yeniden etkinleştiren kod üzerinden zincirlemeyi kullandı. SafeSEH, SEH-overwrite primitive'ini daraltır ama heterojen bir process boyunca onu emekliye ayırmaz.