Intel CET Shadow Stack¶
Her return address'in korumalı ikinci bir kopyasını tutan hardware shadow stack; böylece normal return address'in bir stack-buffer overwrite'ı
RET'te bir control-protection (#CP) fault'u ile yakalanır ve ROP nötralize edilir.
Mechanism¶
Neden çalışır
Return-oriented programming tek bir invariant'a dayanır: RET'in tükettiği
adres, attacker'ın yazabildiği bellekte (normal stack'te) yaşar. CET'in
Shadow Stack'i, return address'in ikinci, hardware tarafından yönetilen bir
kopyasını sıradan store'ların dokunamadığı ayrı bir page'de tutarak bu
invariant'ı kırar.
- Bir
CALL'da, processor return address'i hem normal stack'e hem de shadow stack'e push'lar.RET'te, shadow-stack kopyasını pop'lar ve normal-stack kopyasıyla karşılaştırır. İkisi farklıysa, processor bir control-protection (#CP) fault'u doğurur. (Linux: "processor return address'i hem normal stack'e hem de shadow stack'e push'lar ... ikisi farklıysa, processor bir control-protection fault'u doğurur.") - Shadow stack, özel bir "shadow stack" memory type'ına sahip page'lerde
yaşar. Normal
MOVile yazılamazlar; onlara yalnızca call/return makinesi ve özelWRSS/INCSSP/RSTORSSPinstruction'ları dokunur veWRSSayrıca etkinleştirilmelidir (ARCH_SHSTK_WRSS). Yani kaydedilmiş return address'i yeniden yazan bir heap/stack overflow shadow kopyasını el değmemiş bırakır ve uyuşmazlık kontrol transfer olmadan önce tespit edilir. - Shadow-stack pointer'ı mimari bir register'dır, SSP. Normal bir operand
olarak adlandırılamaz; yazılım onu
RDSSPile okur ve yalnızca kısıtlanmış instruction'larla ayarlar (frame'leri unwind etmek içinINCSSP, stack değiştirmek içinRSTORSSP/SAVEPREVSSPartı restore token'ları). - Etkinleştirme mode başına
IA32_U_CET(user) /IA32_S_CET(supervisor) MSR'ları aracılığıyladır; userspace onuarch_prctl(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)ile açar,CONFIG_X86_USER_SHADOW_STACKveuser_shstkCPU feature'ı ile koşullanır.
Bu not user-mode (ring-3) tarafı açıklar (IA32_U_CET, arch_prctl,
glibc opt-in). Aynı invariant'ı ring 0'a taşıyan — CR4.CET, per-privilege
IA32_PL0..3_SSP MSR'ları ve SETSSBSY/CLRSSBSY busy-bit token'larıyla
yapılandırılan — ayrı supervisor varyantı için bkz.
Intel CET Supervisor Shadow Stack.
Aynı hardware mekanizmasının Windows / Microsoft-mode tarafı — PE-header
opt-in, Get-ProcessMitigation/UserShadowStack policy tooling ve WER/#CP
crash telemetrisi — ayrı bir not'tadır: bkz.
CET Hardware-enforced Stack Protection.
Bu sayfa ise Linux/glibc (arch_prctl, /proc introspection, SSP/token
semantiği) tarafına odaklanır.
Invariant: return hedefi, attacker'ın forge edemeyeceği bellekten alınır.
Bozulmuş bir normal-stack return address'i artık RET'i yönlendiremez;
yalnızca bir fault üretir.
Walkthrough¶
1. Hardware + kernel desteğini doğrula. /proc/cpuinfo'daki user_shstk flag'i
userspace shadow-stack desteğini gösterir; Kconfig seçeneği
X86_USER_SHADOW_STACK'tir ve nousershstk onu boot'ta devre dışı bırakır.
2. Bir binary'nin opt-in yaptığını kontrol et. GCC/Clang, -fcf-protection ile
derlendiğinde bir GNU-property note emit eder; readelf SHSTK feature'ını gösterir:
3. Shadow stack ile başlatılmış bir glibc bunu thread başına raporlar. Kernel,
canlı durumu /proc/$PID/status'ta açar:
$ cat /proc/self/status | grep x86_Thread_features
x86_Thread_features: shstk
x86_Thread_features_locked: shstk
4. Programatik olarak etkinleştir (glibc'nin startup'ının izlediği yol):
#include <asm/prctl.h>
/* feature bits: ARCH_SHSTK_SHSTK, ARCH_SHSTK_WRSS */
arch_prctl(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK);
/* ARCH_SHSTK_LOCK pins the setting; ARCH_SHSTK_STATUS reads it back */
5. Bir ROP girişiminde fault'u gözlemle. Normal stack'teki kaydedilmiş return
address'i overwrite et ve fonksiyonun return etmesine izin ver: shadow kopyası hâlâ
gerçek caller'ı tutar, ikisi uyuşmaz ve CPU #CP teslim eder. Linux bunu, gadget'a
transfer etmek yerine control-protection signal code'lu fatal bir SIGSEGV'e
dönüştürür.
Signal-handling detail (why handlers still work)
On a signal, "the old pre-signal state is pushed on the stack. When shadow
stack is enabled, the shadow stack specific state is pushed onto the shadow
stack" — the old SSP is saved in a special token with bit 63 set, then
verified and restored on sigreturn so the handler's own returns stay
protected.
Kapsam ve artık risk
Shadow Stack yalnızca backward edge'leri (return'leri) korur. Forward-edge
hijack'leri (bir indirect CALL/JMP hedefini bozma) etkilenmez — bu,
Indirect Branch Tracking'in işidir.
Tüm-program kapsaması ayrıca her library'nin -fcf-protection ile derlenmesini
gerektirir; enforcement strict değilse tek bir non-CET DSO return'leri korumasız
bırakabilir.
Mitigation¶
(Artık risk / hâlâ neler geçer.) Return'ler kapsanır, ama bir return address'i hiç bozmayan saldırılar hayatta kalır: saf JOP/COOP forward-edge chain'leri ve gerçek C++ virtual-call site'larını yeniden kullanan o spesifik counterfeit-object bypass. Anlamlı bir control-flow integrity için shadow stack'i IBT (ve FineIBT gibi daha ince forward-edge CFI) ile eşleştirmek gerekir.
References¶
- The Linux Kernel. Control-flow Enforcement Technology (CET) Shadow Stack. — https://docs.kernel.org/next/x86/shstk.html
- The Linux Kernel (v6.6). CET Shadow Stack. — https://www.kernel.org/doc/html/v6.6/arch/x86/shstk.html