X-RDate: Thu, 05 Feb 1998 12:18:12 +0500 (ESK)
Date: Wed, 4 Feb 1998 19:26:17 EST
From: Andy Church <[email protected]>
To: [email protected]Subject: Re: Defeating Solar Designer non-executable stack patch
>An idea which should stop these scribbling attacks cold is this: put
>guard bytes above and below the return address on the stack - and use
>0x00 as the guard bytes. Then the function epilogue/return code checks
>for these 0x00 bytes and if it doesn't find them it does something
>appropriate for a garbaged return address - a deliberate segfault,
>perhaps, or a halt-cpu instruction (which certainly ought to be
>sufficiently illegal for user-land code!). The point, of course, is
>that you would have to find a way to overwrite the return address
>without disturbing the NULs, which is very hard when using routines
>like strcpy or sprintf, the favorites of smash-the-stack attacks, since
>they stop at NULs.
Here's a thought about one way to get around it--though it would only
work in a particular set of circumstances. Suppose someone has a format
string for sprintf() that's not a constant string, i.e. that's in the
data or stack areas. To make things easy, without limiting the usefulness
(more than it already is limited), suppose we have such a string on the
stack right above an automatic buffer variable. This isn't entirely
unreasonable; take the following piece of code, for example (actually
borrowed from an old version of a program of mine, I'm ashamed to say):
log(const char *fmt, ...)
{
va_list args;
char fmtbuf[64];
char buffer[1024];
va_start(args, fmt);
sprintf(fmtbuf, "%s\n", fmt);
vsprintf(buffer, fmtbuf, args);
fputs(buffer, stderr);
fputs(buffer, logfile);
}
Now if we call log("USER: %s", username), for example, we can do the
following interesting piece of trickery. If the username string is more
than 1026 characters long, we'll start overwriting the format string at the
point where sprintf() will pick up when it finishes copying the username
string. I'll leave the details to you, dear readers (translation: I don't
feel like working it out myself now ;) ) -- but if you know there's a \0 on
the stack somewhere nearby, such as the guard bytes for the next routine
up, you can use a %c to paste it into your own string, and terminate your
custom-built format string at the same time.
Having said that, of course, I have to point out that if you choose a
guard value with just one byte (but NOT the least-significant one) equal to
zero, you cut off the most obvious source of fuel for this exploit.
>Does anyone know of any common routine which (a) has an unlimited-size
>interface and (b) is capable of writing user-supplied data containing
>NULs into the target area? (Even if such a routine is found, merely
>getting the exploit string to it intact is nontrivial, since NULs also
>cannot be present in things like environment variables.)
One comes to mind off the top of my head: gets(). Now I seriously
_hope_ nobody actually uses this anymore, given how insecure it is and
the fact that fgets() exists, but a little test shows it happily inputs
\0's along with anything else:
-------- gets.c --------
#include <stdio.h>
int main() {
int j;
volatile int i;
char buf[32];
i = 0;
for (j = 0; j < 32; j++) printf("%02X", buf[j]);
printf(" %08X\n", i);
gets(buf);
for (j = 0; j < 32; j++) printf("%02X", buf[j]);
printf(" %08X\n", i);
return 0;
}
-----------------------
crystal:/tmp> (dd if=/dev/zero bs=1 count=32 ; echo aaa) | ./gets
0000000000000000000000000000000000000000000000000000000000000000 00000000
0000000000000000000000000000000000000000000000000000000000000000 00616161
Oops. So I played around a little, and found out that scanf() and
fscanf() do the same thing with a "%s" format parameter. I _have_ seen
fscanf() pop up in places, but not (yet) anywhere it could be exploited.
So I have a feeling that writing exploit code based on this would have
little practical application.
--Andy Church | If Bell Atlantic really is the heart
[email protected] | of communication, then it desperately
www.dragonfire.net/~achurch/ | needs a quadruple bypass.