Stack canaries
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]
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