Re: Defeating Solar Designer non-executable stack patch

From: Andy Church (achurchat_private)
Date: Wed Feb 04 1998 - 16:26:17 PST

  • Next message: raf@licj..... (Bugtraq Mirror): "imapd/ipop3d coredump - the 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
        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