A few weeks ago we gave a training on secure coding and talked about the stack protector on Linux. While roughly explaining how it works and how it can be utilized with GCC, an attendee asked where the stack canary comes from. The question was quickly resolved by answering that it’s a random value provided by the kernel upon process startup.
But where does the value really come from? This question turned out to be more interesting than expected. In this blog post we will give you a rough overview where the stack canary comes from with different libc implementations.
Stack canaries are a security feature implemented in many software programs to
help mitigate buffer overflow attacks. The stack protector places a
randomized canary value at a specific position on the function stack.
It is crucial that the value is not known to the attacker, not can be easily guessed.
This is why the canary value is randomly selected for each newly executed program.
Please note that the value is not unique per process, if a process is copied,
i.e. via fork()
, then also the canary value stays the same.
Before function returns the value is compared to the initial one. If it changed, the
stack is corrupted and execution aborts to prevent an attacker from executing their malicious code.
For example, in GCC the stack protector can be enabled using the
-fstack-protector
compiler option.
Let’s take a look at how various stack protector implementations select the canary value.
In most implementations, the symbol __stack_chk_guard
holds the canary value,
and the function __stack_chk_fail()
is called upon failure.
libssp is a standalone library included in GCC, if enabled, providing stack protection mechanisms. Per default, it is disabled on most Linux systems, but it has proven to be useful on bare metal (embedded) systems without a libc.
To generate its canaries, it tries to read from /dev/urandom
if present. Otherwise, the canary is hard-coded to the values 0x00
, 0x00
, 0xff
, 0x0a
. These values are chosen to stop string functions and prevent stack overflow:
0x00
is the NULL character, terminating a string0xff
is invalid according to the UTF-8 specification0x0a
is a newline characterRunning gcc -v
on your distro will most likely show --disable-libssp
.
Source:
gcc/libssp/ssp.c:73
When there is no libssp
support is available, glibc can handle canaries on its own.
For each newly started program, the kernel generates a random
value and passes it to the program using the auxiliary vector1 AT_RANDOM
from the userspace’s
auxiliary vector. On non-Linux systems, glibc falls back to using the value 0x00
, 0x00
, 0xff
, 0x0a
(see libssp).
To retrieve the value for the current execution, set LD_SHOW_AUXV=1
.
$ LD_SHOW_AUXV=1 /bin/true | grep AT_RANDOM
AT_RANDOM: 0x7ffcacfa8c49
Source:
glibc/sysdeps/unix/sysv/linux/dl-osinfo.h:26
The lightweight alternative C library musl uses AT_RANDOM
for its stack
canaries, just like glibc. As a fallback on non-Linux it uses the address of
__stack_chk_guard
multiplied by a magic value (0x41C64E6D
), which may
seem suboptimal at first glance.
However, if ASLR is available, it can serve as a source of randomness. ASLR randomizes memory addresses, making it challenging for attackers to exploit memory-related vulnerabilities. By relying on ASLR to randomize the stack canary value in the fallback scenario, it ensures that the canary is not easily guessable.
Source:
musl/src/env/__stack_chk_fail.c:7
The bionic libc, utilized by Google on Android, uses an arc4 random
number generator if the /dev/urandom
pseudo-device is available. If not, it
falls back to using AT_RANDOM
through the auxiliary vector.
Source:
bionic/libc/bionic/__libc_init_main_thread.cpp:109
uclibc-ng is a small C library primarily designed for embedded systems. For the
random number generation of its stack protector it utilizes /dev/urandom
by
default. Alternatively, it can also fall back to a pseudo-random approach based on
a magic value (0xFF0A0D00UL
) xor current time.
Source:
uclibc-ng/libc/sysdeps/linux/common/dl-osinfo.h:36
The statically linked C library, developed by fefe, also
takes hold of AT_RANDOM
for stack canary generation, with an optional
fallback to /dev/urandom
.
Source: dietlibc/lib/stackgap-common.h
picolibc is another C library primarily used on embedded systems, particularly in scenarios with very limited main memory. Per default, it uses the
magic value 0x00
, 0x00
, 0xff
, 0x0a
(see libssp). If feasible and constructors are available, it utilizes the random number generator of the operating system.
Source:
picolibc/newlib/libc/ssp/stack_protector.c:21
The Linux kernel itself incorporates a stack protector mechanism for each kernel stack. It obtains random numbers from its internal random number generator.
Source:
linux/include/linux/stackprotector.h:23
OpenBSD’s libc previously used the getentropy()
system call to obtain random values
in its stack canaries, which recevied its bytes directly from the kernel’s
entropy pool. However, starting from the 5.3 release, they transitioned to .openbsd.randomdata
,
which is an ELF section filled by the kernel on program execution (using its
entropy pool).2
Source:
openbsd-src/lib/libc/sys/stack_protector.c:47
The variations in the implementation details of stack protectors and the generation of its stack canaries across diverse C libraries are remarkably diverse. Some C libraries utilize the (pseudo) random number generator of the OS, while others have their own implementations.
Generally, C libraries follow quite sane defaults. However, the main concern lies in the fallbacks: They are often questionable. One possible attack vector would be to ensure that the C library falls back to a (less secure) source of random numbers.
Publish date
30.05.2023
Category
security
Authors
Aaron Marcher
Richard Weinberger
+43 5 9980 400 00 (email preferred)
sigma star gmbh
Eduard-Bodem-Gasse 6, 1st floor
6020 Innsbruck | Austria