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

  1. https://www.coresecurity.com/sites/default/files/private-files/publications/2016/05/StackguardPaper.pdf
  2. http://phrack.org/issues/56/5.html
  3. https://wiki.osdev.org/Stack_Smashing_Protector
  4. https://ctf101.org/binary-exploitation/stack-canaries/
  5. https://spotless.tech/angstrom-ctf-2020-Canary.html
  6. https://dunsp4rce.github.io/redpwn-2020/pwn/2020/06/26/dead-canary.html