TL;DR: It is possible to defeat stack canary protection when a binary is vulnerable to arbitrary file read.
First of, Happy New Year 2017 ✌
Recently, I’ve decided to thoroughly investigate the “Stack Smashing
Protection” (SSP) on recent Linux and recent Glibc. This research has led to a
blog post available on elttam R&D blog. Among
many other things, I’ve found that canaries built with recent glibc may have
their values leaked, should the program be also vulnerable to an arbitrary file
read access, and if it exposes its
Auxiliary Vector via
All the details regarding the following attack on the canary are explained in this blog post, so I will assume that you are familiar with it. If you’re not:
In the article, I imagined the attack scenario would apply perfectly well to a Web or FTP server, and would occur following those steps:
/proc/self/auxvto get the
/proc/self/memand force an
lseekaccess to reach the location found above via the HTTP header Range (for instance
- Truncate the received buffer to
- Nullify the last byte (
result &= ~0xff)
That was the theory, which made perfect sense, but I wanted a practice case.
You can download:
This cheap patch provides to the “new”
websrv the (pseudo-)capability to
parse the HTTP Range header
provided by the client. This is basically how modern Web servers (Apache, nginx)
treat this header.
In the earlier exploit, we had exploited the Directory Traversal to dump the
process memory mapping (via
/proc/self/maps) and defeat PIE & ASLR. To crush
SSP protection, we managed to get the canary value by brute-forcing it, which is
very noisy (the canary can be found in max. of 4*256=1024 HTTP requests on a
32-bit architecture, 2048 on 64-bit) and risky (the memory corruption may alert
of an on-going attack).
But now we can actually do much better: we have all the conditions mentioned earlier to exfiltrate the canary’s value, thanks to the ELF Auxiliary Vector.
This approach is a lot more stable and stealthier than canary brute-forcing, since we don’t rely on any memory corruption/process crash to determine the valid bytes of the canary as we did before.
Find AT_RANDOM from the Auxiliary Vector
So first, we need to read the process Auxiliary Vector exposed via
And then parse the result:
If we did things correctly, this will store in the variable
the address of the 16 random bytes provided by the kernel, used to create the
(l)seeking the process memory via the HTTP Range header
procfs also exposes the process memory, we can use
seek to the address we’ve found at the step above.
yama/ptrace_scope must be set to 0 to be able to read the process
The final exploitation script which combines all the steps described above can be found here.
To be we fetched the correct value for the canary of the remote process, we can use this script locally to compare the values for the canary:
This exploitation shows a different way to leak the canary value, and therefore defeat the SSP protection. As you may have noticed, since this attack does not rely on memory corruption, it is extremely reliable. And it is also much faster: the canary brute-force can take up 4x256 (or resp. 8x256 for 64-bits) requests to determine, where this approach found the same value with only 2 requests.
This illustrates once again the need to maintain a system as hardened as
possible, especially on production systems, since restricting
refusing to expose AUXV like GrSec does, would defeat this attack.
Thanks for reading, and as usual drop me a line on IRC/Twitter/email for any question/comment ☕
Share this post: