Update on canary randomisation for hardened Linux applications
This article is a followup to our last year’s advisory on canary randomisation for applications of the Debian distribution.
I was recently asked what the currently employed method is for canary randomisation in SSP-armoured Linux applications. I’ve been meaning to write an article on this for some time now, but didn’t have the necessary time. So here it is (albeit a little late).
To recap our previous article on the subject, Debian GNU/Linux used to build the GNU libc packages without the “—enable-stackguard-randomization” flag. This meant that all SSP-armoured applications got a static (i.e. predictable) canary.
Our suggestion at the time was to add the “—enable-stackguard-randomization” flag to the Debian glibc build-time options. Unfortunately, this solution incurred a significant penalty in system performance, since all applications (even the ones not using SSP) had to open /dev/urandom to get their random canary bytes.
Kees Cook (of canonical.com) had been pushing for a kernel-based solution to this problem since late 2008 [1]. What Kees (and others) saw as an efficient alternative was to initialise the canary with data coming from a kernel-supplied array of random bytes. The proposed array, called AT_RANDOM, would be part of the ELF Auxiliary Vector and could also be of use to other applications, requiring fast access to small amounts of random data. For our non-ELF-expert readers, I should note that the ELF Auxiliary Vector is a mechanism for passing system information (e.g. memory page size, executable name etc.) to an application. The ELF Auxiliary Vector resides within the application stack (actually, right after the array hosting the environmental variables).
The AT_RANDOM array was indeed added to the Linux kernel in January 8th, 2009 [2]. Three days later, GNU libc received the appropriate patch so that the canary value would be initialised to the first word of random data found in the AT_RANDOM array [3].
Regarding SSP, everything else continued working exactly in the same way: The SSP ABI for the x86 architecture instructs that the compiler will find the random canary value at “%gs:0x14”. So, in dynamically linked executables, the dynamic loader will initialise “%gs:0x14” with the first word of the AT_RANDOM array, while the same operation will be performed by __libc_start_main in statically linked executables.
GNU libc uses the __ASSUME_AT_RANDOM symbol to identify platforms that support the AT_RANDOM array of random bytes. At the time of this writing, this only holds true for Linux. On other platforms one will still have to use the —enable-stackguard-randomization flag to get a random canary (through /dev/urandom).
You can find out if your system supports the AT_RANDOM canary initialisation method by searching for the AT_RANDOM array reference like this:
$ LD_SHOW_AUXV=1 /bin/true | grep AT_RANDOM
AT_RANDOM: 0xbfd6b1bb