DoS caused by lpd

From: Kevin K. Sochacki (kksochaat_private)
Date: Fri Dec 18 1998 - 11:55:28 PST

  • Next message: the razor of love: "OSS nice tmp race"

    This is a multi-part message in MIME format.
    --------------C6BADB5BD78AAB687CDA832B
    Content-Type: text/plain; charset=us-ascii
    Content-Transfer-Encoding: 7bit
    
    Hi all,
    
    Denial of Service caused by lpd.
    
    The lpd in question is packaged with RedHat (lpr-0.31-1 or
    lpr-0.33-1).   I did not look at lpd on any other plantform, so they may
    or may not have the problem.
    
    The problem was reported back on July 28, 1998 by Martin Lacasse
    mdlacasat_private to RedHat or more specificlly msfat_private  and
    ewtat_private for lpr-0.31-1 and yet lpr-0.33-1 was released with the
    same problem.
    
    THE CAUSE
    When a remote user prints to a queue that is _not_ restricted (no rs
    capability in printcap) on a print server they don't have an account on,
    lpd will try to fork the filter as that user.  By this time lpd has
    already established a connection to the printer, not being able to fork
    because user is not found the connection presist tying up the printer so
    that other TCP connection are refused hence the first denial of
    service.  Also the job remains at the top of the queue jaming the queue
    so other request are accepted but not printed hence the second denial of
    service.  Also in looking at the code to hack a fix I noticed that
    althought lpd attempts not to run the filters as root if the remote or
    local user is root the filters are run as root.  This leaves the filters
    open to race condition and the like on poorly writen filters.
    
    THE FIX
    Attached is a patch to printjob.c where the problem was found.   The fix
    was if user did not exist or is root effectively become lp to fork the
    filters other wise fork the filter as the user.
    
    THE PROOF
    First let me appologize for the log prompts, but I like to know when,
    what, whence, who and where at a glance.
    Also the hostnames and IPs have been xxxed out to hide their IDs,
    security you know.
    
    #testprint user _does_not_ exist on server
    #kksocha user does exist on the server
    #both printer job are remote request
    #the printcap _does_not_ have a 'rs' for this queue
    
    #as you can see a small 124 byte print request has be made by testprint
    user
    #and the lpd has a connection hence the sending to reported by lpq
    
       Mon Dec 14 16:33:05 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxprint1]$ lpq -Pxxx01-cc103
       xxprint1.erenj.com: sending to xxx01-cc103
       Rank   Owner      Job  Files                                 Total
    Size
       1st    testprint  11   .bashrc                               124
    bytes
       2nd    kksocha    12   .bashrc                               682
    bytes
       0 bytes
    
       connection to xxx01-cc103 is down
    
    #any attempted to connet to the printer port of the printer is refused
    as long as lpd is sending
    #however lpd can't fork the filter to actually send the data because the
    user _does_not_ exist
    #oh port 9100 is also refused but I don't show that here
    
       Mon Dec 14 16:33:53 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxprint1]$ telnet xxx01-cc103 printer
       Trying xxx.xxx.1.167...
       telnet: Unable to connect to remote host: Connection refused
    
    #over 2 minutes later lpd is still trying to  send 124 bytes to printer
    which is more then enough
    #time as will be proven later with the second larger job in the queue
    
       Mon Dec 14 16:35:36 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxprint1]$ lpq -Pxxx01-cc103
       xxprint1.erenj.com: sending to xxx01-cc103
       Rank   Owner      Job  Files                                 Total
    Size
       1st    testprint  11   .bashrc                               124
    bytes
       2nd    kksocha    12   .bashrc                               682
    bytes
       0 bytes
    
       connection to xxx01-cc103 is down
    
    #remove the job that is jamming the queue and tying up the connection to
    the printer
    
       Mon Dec 14 16:36:12 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxrint1]$ lprm -Pxxx01-cc103 11
       dfA011Aa07949 dequeued
       cfA011dXXXXXXenj.com dequeued
       dfA011Aa14026 dequeued
       cfA011xxnk.erenj.com dequeued
    
    #lpd is now sending the second job to the printer (and their are already
    no entries)
    
       Mon Dec 14 16:36:40 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxprint1]$ lpq -Pxxx01-cc103
       xxprint1.erenj.com: sending to xxx01-cc103
       Rank   Owner      Job  Files                                 Total
    Size
       1st    kksocha    12   .bashrc                               682
    bytes
       0 bytes
    
       JetDirect lpd: no entries
    
    #only 7 seconds to send 682 bytes to the printer
    
       Mon Dec 14 16:36:47 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxprint1]$ lpq -Pxxx01-cc103
       JetDirect lpd: no entries
    
    #oh look we can now connet to the printer port of the printer
    
       Mon Dec 14 16:36:56 </dev/ttyp0> /usr/src/redhat/BUILD/lpr-0.33/lpd
       [root@xxprint1]$ telnet xxx01-cc103 printer
       Trying xxx.xxx.1.167...
       Connected to xxx01-cc103.erenj.com.
       Escape character is '^]'.
       ^]
       telnet> close
       Connection closed.
    
    #after my hack every thing worked fine including not fork filters as
    root
    
    Thanks for your attention..........
    
    --
    (...Later..:)
             _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
            _/                        _/ Exxon Research & Engineering   _/
           _/ _/_/_/ _/_/     _/_/_/ _/ Kevin K. Sochacki              _/
          _/ _/     _/  _/   _/     _/ ICSBS CC124 (908) 730-2911     _/
         _/ _/_/_/ _/_/   & _/_/_/ _/ mailto:kksochaat_private       _/
        _/ _/     _/ _/    _/     _/ PERSONAL                       _/
       _/ _/_/_/ _/  _/   _/_/_/ _/ mailto:kksat_private       _/
      _/                        _/ http://mars.superlink.net/kks  _/
     _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    
    
    
    --------------C6BADB5BD78AAB687CDA832B
    Content-Type: text/plain; charset=us-ascii; name="printjob.patch"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline; filename="printjob.patch"
    
    81a82
    > static char   euser[32];              /* effective user */
    368a370,383
    >                       } else {                        /* not restricted */
    >                               struct passwd *dude;
    >                               dude = getpwnam(logname);
    >                               if (dude == NULL || dude->pw_uid == 0) {
    >                                       /* if user doesn't exist will cause a DoS so set    */
    >                                       /* effective user to lp, also we don't want to be   */
    >                                       /* root either to prevent race condition and buffer */
    >                                       /* overrun and the like in poorly designed filters  */
    >                                       /* don't change logname so mail & banner are right  */
    >                                       strncpy(euser, "lp", 3);
    >                               } else {
    >                                       /* since the user does exist run filter as user */
    >                                       strncpy(euser, logname, sizeof(logname));
    >                               }
    526c541
    <       if (getpwnam(logname) == (struct passwd *)0 || !*logname)
    ---
    >       if (getpwnam(euser) == (struct passwd *)0 || !*euser)
    553c568
    <               if ((prchild=doforkuser(DORETURN,logname)) == 0) { /* child */
    ---
    >               if ((prchild=doforkuser(DORETURN,euser)) == 0) { /* child */
    671c686
    <       if ((child = doforkuser(DORETURN,logname)) == 0) {      /* child */
    ---
    >       if ((child = doforkuser(DORETURN,euser)) == 0) {        /* child */
    810a826
    >                       logname[sizeof(logname) - 1] = '\0';
    815a832,845
    >                       } else {                        /* not restricted */
    >                               struct passwd *dude;
    >                               dude = getpwnam(logname);
    >                               if (dude == NULL || dude->pw_uid == 0) {
    >                                       /* if user doesn't exist will cause a DoS so set    */
    >                                       /* effective user to lp, also we don't want to be   */
    >                                       /* root either to prevent race condition and buffer */
    >                                       /* overrun and the like in poorly designed filters  */
    >                                       /* don't change logname so mail & banner are right  */
    >                                       strncpy(euser, "lp", 3);
    >                               } else {
    >                                       /* since the user does exist run filter as user */
    >                                       strncpy(euser, logname, sizeof(logname));
    >                               }
    851c881
    <                           if ((ifchild = doforkuser(DORETURN,logname)) == 0) { /*child*/
    ---
    >                           if ((ifchild = doforkuser(DORETURN,euser)) == 0) { /*child*/
    
    --------------C6BADB5BD78AAB687CDA832B--
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:25:13 PDT