StackGuard Mechanism: Emsi's Vulnerability
A significant security vulnerability has been discovered by Mariusz
Woloszyn <emsi@it.pl> that permits attackers to perpetrate successful
attacks against StackGuarded programs under particular circumstances.
Woloszyn is preparing a Phrack article
describing this vulnerability, which we summarize here. StackGuard
1.21 effectively protects against this vulnerability. The Immunix
team would like to thank Mariusz for kindly notifying us first about this
vulnerability, and allowing us the time to develop and distribute a defense.
The Problem
Consider this vulnerable code:
foo(char * arg) {
char * p = arg; // a vulnerable pointer
char a[25]; // the buffer that makes the pointer vulnerable
gets(a); // using gets() makes you vulnerable
gets(p); // this is the good part
}
In attacking this code, the attacker first overflows the buffer a[]
with a goal of changing the value of the char * p pointer.
Specifically, the attacker can cause the p pointer to point anywhere
in memory, but especially at a return address record in an activation
record. When the program then takes input and stores it where p
points, the input data is stored where the attacker said to store
it.
The above attack is effective against the Random and Terminator Canary
mechanisms
because those methods assume that the attack is linear, i.e.
that an attacker seeking to corrupt the return address must necessarily
use a string operation that overflows an automatic buffer on the stack,
moving up memory through the canary word, and only then reach the return
address entry. The above attack form, however, allows the attacker
to synthesize a pointer to arbitrary space, including pointing directly
at the return address, bypassing canary protection.
The Solution: The XOR Random Canary
StackGuard 1.21 introduces a new canary
defense mechanism: the XOR Random canary. Like the random canary
mechanism, we choose a vector of 128 random canary words at exec()
time, but we also XOR the canary with the return address word, so
that the return address is bound to the random canary value.
The exact procedure is as follows:
-
Setting up an activation record: when calling a function
-
push the return address
-
look up the random canary word for the current function
-
XOR the random canary word with the return address
-
store the result immediately below the return address in the activation
record
-
Tearing down an activation record: when returning from a function
-
fetch the canary word from memory
-
XOR the memory canary word with the return address on the stack
-
compare the result with the random canary word associated with the current
function
The result of this method is that we have the same protection as with the
classic Random canary, and also the property that the attacker cannot
modify the return address without invalidating the canary word.
Availability
StackGuard 1.21 has been made available.
We have done partial testing with this compiler, using it to build many
programs common in Linux distributions, and have not observed any problems.
However, we have not yet done a complete build of an entire
Linux distribution, so this compiler should be considered beta for
now.