Stack canaries or stack cookies are values that are placed onto the stack to monitor and protect sensitive information, such as the frame pointer or a function’s return address, from buffer overflows or stack smashing. Stack canaries are usually placed between buffers and control data on the stack. The terminology for stack canaries is derived from the historic practice of using canaries in coal mines. Canaries would be affected by toxic gases much earlier than the miners, providing a biological warning system.
Stack canary implementations
Multiple stack canary implementations exist, starting from their inception in the early 2000’s. Stack canaries were created because buffer overflow exploits were very popular at the time. They are one of the many different protection mechanisms that were created in an attempt to thrwart the exploitation of this vulnerability class. I’m only going to cover the most prominent examples.
StackGuard
StackGuard was a modification for GCC, adding new code to function prologues and
epilogues. StackGuard pushes a terminator canary onto the stack and checks
the value of the terminator canary before conducting the final ret
of the
function.
A terminator canary is a canary that contains characters usually used to
terminate strings or input from functions like gets()
, these characters being:
0x000aff0d
. The thought process behind StackGuard and terminator canaries is
that the attacker will be unable to overwrite the return address on the stack to
gain control of the EIP
/RIP
if their payload must include these bad
characters - the input to overwrite the return address will be terminated too
early.
If the attacker somehow manages to mangle StackGuard’s terminator canary, the
canary check will fail at the function epilogue and the program will jump to
__canary_death_handler
, exiting execution. [1]
Bypassing StackGuard
Referencing this Phrack article,
StackGuard may protect the return address of the function on the stack, however
, it neglects to protect the local variables from overwriting each other. In the
example, two strcpy()
operations are conducted with one use of strcpy()
being vulnerable to an out-of-bounds write. Using this out-of-bounds write, the
attacker implements an arbitrary write primitive to overwrite the return
pointer. This probably wouldn’t work with ASLR enabled, so a secondary option
would probably include overwriting some function pointer in the .plt.got
section to gain code execution - this is only useful if the binary executable is
not a PIE.
Another issue with StackGuard is that it does not protect the frame pointer on the stack, allowing an attacker the ability to conduct a stack pivot. [1]
Stack Smashing Protector
Stack Smashing Protector (SSP) is a GCC compiler feature that detects stack
buffer overflows and aborts if the secret value (canary) on the stack is
changed. The stack canary used by SSP is randomly generated using
/dev/urandom
, and the canary is a different value each time the program is
invoked.
The SSP is pretty easy to notice when disassembling a program. At the beginning
of each function prologue, the stack canary is acquired from thread local
storage and pushed onto the stack after the frame pointer. At the end of the
function epilogue, prior to calling leave
or ret
, the value of the canary is
fetched from the stack and compared against the value saved in thread local
storage with an XOR
operation. If the outcome of this XOR
operation is not
0
, we have failed the canary check and the __stack_chk_fail
function is
called, exiting program execution. [3]
Bypassing SSP
There are multiple methods to bypass SSP, but some conditions must exist. Some crowd favorites are:
- Using a format string vulnerability to conduct a sensitive information leak in order to leak the value of the canary. [5]
- Using an arbitrary write primitive to overwrite the value of
__stack_chk_fail
in the global offset table and then intentionally failing the canary check in order to gain code execution. [6] - If a server process uses
fork()
to handle multiple connections, attackers can brute force the value of the canary because the value of the canary will not change for child processes. [4]
Related pages
References
- https://www.coresecurity.com/sites/default/files/private-files/publications/2016/05/StackguardPaper.pdf
- http://phrack.org/issues/56/5.html
- https://wiki.osdev.org/Stack_Smashing_Protector
- https://ctf101.org/binary-exploitation/stack-canaries/
- https://spotless.tech/angstrom-ctf-2020-Canary.html
- https://dunsp4rce.github.io/redpwn-2020/pwn/2020/06/26/dead-canary.html