216 - Busted Stack Protectors, MTE, and AI Powered Fuzzing
A bit of an unexpected fault in GCC’s -fstack-protector
implementation that meant that the saved return address wasn’t actually protected by the stack-protector on AArch64 in some cases.
If you’re unfamiliar the way stack protector works is that it’ll inject a local variable at the top of the locals section containing a canary value. Before returning it will check the canary value and if it changed it knows a stack-based corruption happened. With a normal stack-frame the saved return address will be before the canary, so a linear overflow starting at a local address will have to overwrite the canary in order to reach the saved return address for the traditional control-flow hijacking attack.
The problem is that the AArch64 stack-frame is a bit different in two important ways:
- The saved registers like the saved return address are placed after the locals instead of before.
- Dynamic stack allocations (like those from an
alloca
call) are stored after the saved registers
This creates a situation where if a linear overflow starts from one of the dynamic stack allocations it can overflow into the saved return address without needing to overflow a canary value