hacktricks/binary-exploitation/common-binary-protections-and-bypasses/stack-canaries/README.md
Carlos Polop 731fca1d57 a
2024-07-18 18:14:56 +02:00

8.6 KiB

Stack Canaries

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

StackGuard and StackShield

StackGuard inserts a special value known as a canary before the EIP (Extended Instruction Pointer), specifically 0x000aff0d (representing null, newline, EOF, carriage return) to protect against buffer overflows. However, functions like recv(), memcpy(), read(), and bcopy() remain vulnerable, and it does not protect the EBP (Base Pointer).

StackShield takes a more sophisticated approach than StackGuard by maintaining a Global Return Stack, which stores all return addresses (EIPs). This setup ensures that any overflow does not cause harm, as it allows for a comparison between stored and actual return addresses to detect overflow occurrences. Additionally, StackShield can check the return address against a boundary value to detect if the EIP points outside the expected data space. However, this protection can be circumvented through techniques like Return-to-libc, ROP (Return-Oriented Programming), or ret2ret, indicating that StackShield also does not protect local variables.

Stack Smash Protector (ProPolice) -fstack-protector:

This mechanism places a canary before the EBP, and reorganizes local variables to position buffers at higher memory addresses, preventing them from overwriting other variables. It also securely copies arguments passed on the stack above local variables and uses these copies as arguments. However, it does not protect arrays with fewer than 8 elements or buffers within a user's structure.

The canary is a random number derived from /dev/urandom or a default value of 0xff0a0000. It is stored in TLS (Thread Local Storage), allowing shared memory spaces across threads to have thread-specific global or static variables. These variables are initially copied from the parent process, and child processes can alter their data without affecting the parent or siblings. Nevertheless, if a fork() is used without creating a new canary, all processes (parent and children) share the same canary, making it vulnerable. On the i386 architecture, the canary is stored at gs:0x14, and on x86_64, at fs:0x28.

This local protection identifies functions with buffers vulnerable to attacks and injects code at the start of these functions to place the canary, and at the end to verify its integrity.

When a web server uses fork(), it enables a brute-force attack to guess the canary byte by byte. However, using execve() after fork() overwrites the memory space, negating the attack. vfork() allows the child process to execute without duplication until it attempts to write, at which point a duplicate is created, offering a different approach to process creation and memory handling.

Lengths

In x64 binaries, the canary cookie is an 0x8 byte qword. The first seven bytes are random and the last byte is a null byte.

In x86 binaries, the canary cookie is a 0x4 byte dword. The first three bytes are random and the last byte is a null byte.

{% hint style="danger" %} The least significant byte of both canaries is a null byte because it'll be the first in the stack coming from lower addresses and therefore functions that read strings will stop before reading it. {% endhint %}

Bypasses

Leaking the canary and then overwriting it (e.g. buffer overflow) with its own value.

  • If the canary is forked in child processes it might be possible to brute-force it one byte at a time:

{% content-ref url="bf-forked-stack-canaries.md" %} bf-forked-stack-canaries.md {% endcontent-ref %}

  • If there is some interesting leak or arbitrary read vulnerability in the binary it might be possible to leak it:

{% content-ref url="print-stack-canary.md" %} print-stack-canary.md {% endcontent-ref %}

  • Overwriting stack stored pointers

The stack vulnerable to a stack overflow might contain addresses to strings or functions that can be overwritten in order to exploit the vulnerability without needing to reach the stack canary. Check:

{% content-ref url="../../stack-overflow/pointer-redirecting.md" %} pointer-redirecting.md {% endcontent-ref %}

  • Modifying both master and thread canary

A buffer overflow in a threaded function protected with canary can be used to modify the master canary of the thread. As a result, the mitigation is useless because the check is used with two canaries that are the same (although modified).

Moreover, a buffer overflow in a threaded function protected with canary could be used to modify the master canary stored in the TLS. This is because, it might be possible to reach the memory position where the TLS is stored (and therefore, the canary) via a bof in the stack of a thread.
As a result, the mitigation is useless because the check is used with two canaries that are the same (although modified).
This attack is performed in the writeup: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads

Check also the presentation of https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 which mentions that usually the TLS is stored by mmap and when a stack of thread is created it's also generated by mmap according to this, which might allow the overflow as shown in the previous writeup.

  • Modify the GOT entry of __stack_chk_fail

If the binary has Partial RELRO, then you can use an arbitrary write to modify the GOT entry of __stack_chk_fail to be a dummy function that does not block the program if the canary gets modified.

This attack is performed in the writeup: https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/

References

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}