Writing to Shadow Stacks
Where
Main Stage
DEF CON 34
Duration
44 min
Shows three ways to write attacker-chosen return addresses into Linux shadow stacks, undermining Intel CET and ARM GCS backward-edge protection.
Summary
Shadow stacks, as in Intel CET and ARM GCS, are meant to make return-address corruption a dead end by keeping a hardware-checked second copy of return addresses. The talk asks whether that copy can be written from user space. It starts with how the kernel marks and installs shadow stack pages, and why certain kernel write paths can reach them.
The speaker then presents three bypasses. The first abuses the /proc/self/mem write path, where a force flag lets a process write to its own shadow stack pages with open() and pwrite(). The second uses userfaultfd: a shadow stack page is discarded with MADV_DONTNEED, and a fault handler supplies a replacement page filled with chosen return addresses, because the copy path skips a check that the move path enforces. The third uses the WRSSQ instruction, and the speaker corrects a widely copied encoding that actually decodes to a different instruction. The techniques were validated on Sapphire Rapids and against CVEs in dnsmasq, libinput and rsync, including a root shell with CET still enabled.
The first two kernel paths were patched after disclosure, though a fork-and-ptrace variant still works on patched kernels and WRSSQ is a CPU instruction that cannot be patched. The speaker argues that memory management gaps in subsystems written before kernel 6.6 remain a concern, and suggests sealing shadow stack mappings as a mitigation. Practitioners should treat the backward edge as writable on some systems and keep older exploitation techniques in mind.
Materials
Related talks
Main Stage·25 min
Omri Ben Bassat
Shows how sigreturn can bypass ARM64 BTI and PAC on Linux and Android, and how it chains with COOP and coroutine attacks for code execution.
Main Stage·37 min
Daniel Hillel · Noam Trobishi
Shows how a use-after-free in the NVIDIA Linux kernel module lets an unprivileged container user escape to root on the host.
Main Stage·37 min
Can Ozian · Kagan Capar
Follows a mistriaged PHP fuzzer crash from a core Zend use-after-free to a command run on stock PHP despite ASLR and disable_functions.


