>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 achurchat_private | of communication, then it desperately www.dragonfire.net/~achurch/ | needs a quadruple bypass.
This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 13:41:39 PDT