CET shadow stack
TL;DR: Intel CET adds a CPU-maintained shadow stack of return addresses; every
retcross-checks the regular stack against the shadow and#CPs on mismatch — classic ROP that overwrites saved-RIP dies on the first chained gadget.
What it is
Control-flow Enforcement Technology (CET) is an Intel ISA extension (Tiger Lake+, AMD Zen 3+) with two parts: Shadow Stack (SS) for backward-edge protection and Indirect Branch Tracking (IBT) for forward edges. Windows exposes shadow stack as “Hardware-enforced Stack Protection” (HSP) since Windows 10 20H1 / 21H1. When a thread is CET-enabled, every call pushes the return address onto a separate, supervisor-readable-only shadow page; every ret pops both stacks and faults (#CP) if they disagree.
Preconditions / where it applies
- CET-capable CPU (Intel 11th gen+, AMD Zen 3+) and Windows 10 20H2+/Server 2022 with HSP enabled
- Per-process opt-in: the binary’s loader config /
/CETCOMPATlink flag plus the process mitigation policyProcessUserShadowStackPolicy - User mode only (kernel CET shipped with Windows 11 22H2 — see cfg-cet-kernel)
Technique
Attacker workarounds when CET is on:
- Pivot to JOP / COP — jump-oriented and call-oriented programming use indirect
jmp/call, which the shadow stack does not check. IBT (forward edge) blocks them unless anendbr64precedes the target, so chains have to land on legitimate IBT-tagged targets. - Data-only attacks — corrupt heap metadata, vtable slots, or app-level state to reach RCE without ever returning into a gadget.
- Disable shadow stack — overwrite the
ssp(Shadow Stack Pointer) viawrss/rstorsspif the process exposes a primitive; or callSetThreadInformation(ThreadDynamicCodePolicy)-style APIs from within the process to relax CET (usually requires existing arbitrary call). - Find a non-CET module — a single DLL loaded into the process without
/CETCOMPATdisables the policy for the whole process unlessSTRICT_MODEis set. - Race the SSP — research has demonstrated TOCTOU on
rstorssp/saveprevsspsequences during signal-like transitions.
1
2
3
4
5
// Querying CET state at runtime
PROCESS_MITIGATION_USER_SHADOW_STACK_POLICY p = {0};
GetProcessMitigationPolicy(GetCurrentProcess(),
ProcessUserShadowStackPolicy, &p, sizeof(p));
// p.EnableUserShadowStack, p.EnableUserShadowStackStrictMode
Detection and defence
- Enable HSP in audit mode first (
Set-ProcessMitigation -Name foo.exe -Enable UserShadowStack,UserShadowStackStrictMode); WER reports#CPfaults asSTATUS_STACK_BUFFER_OVERRUNwith shadow-stack tag - Combine CET with CFG (forward edge) and ACG (no dynamic code) — see control-flow-guard
- Ensure every loaded module is
/CETCOMPAT; deny-list non-compat DLLs viaImage Loadexploit-guard rules - Kernel: HVCI raises the bar further by preventing the attacker from forging shadow-stack pages — see hvci-vbs
References
- Intel CET specification — ISA reference
- Microsoft: Hardware-enforced Stack Protection — Windows implementation
- Connor McGarr: CET internals — practical analysis and bypass exploration
Related: control-flow-guard, xfg, rop-chains, cfg-cet-kernel