Re: easy DoS in most RPC apps

From: Bill Paul (wpaulat_private)
Date: Sun May 17 1998 - 12:48:55 PDT

  • Next message: David Zhao: "simple kde exploit fix"

    Of all the gin joints in all the towns in all the world, Scott Stone had
    to walk into mine and say:
    
    > On Sun, 17 May 1998, David LeBlanc wrote:
    >
    > > At 02:35 AM 5/15/98 +0200, Peter van Dijk wrote:
    > > >Finally, I'm quite sure of this: the bug is in Sun's RPC code.
    > > >Investigations show Linux, FreeBSD, SunOS, System V and NeXTstep machines
    > > >are affected, which means we've got a _big_ problem here.
    > >
    > > If that's the case, then any ports of these utilities running on Windows NT
    > > would also exhibit the same problem - we're all running off of pretty much
    > > the same Sun ONC RPC code.
    > >
    >
    > The FreeBSD people have already made a patch for this, check their home
    > site.  I'm going to attempt to port the patch to Linux, as the base code
    > should be about the same.. the fix is to a couple of rpc-related files in
    > the C libraries.
    
    (I thought I sent a message about this to the list on Friday but it seems
    not to have made it. Either I sent it off into /dev/null without realizing
    it or gremlins ate it. In either case, I'll try again.)
    
    The modifications I made to the FreeBSD RPC library prevent an attacker
    from completely wedging a stream based RPC service for an indefinite
    period, however there really ought to be more done to avoid the problem
    completely.
    
    The real problem is in the XDR record marking code which is used for
    the TCP transport. (In RPC 4.0, TCP is the only transport affected. In
    TI-RPC, any 'virtual circuit' transport including but not limited to TCP
    is affected.) The set_input_fragment() routine in src/lib/libc/xdr/xdr_rec.c
    attempts to read a record header which is supposed to specify the size
    of the record that follows. Unfortunately, this routine performs no
    sanity checking: if you telnet to a TCP service and send a few carriage
    returns, set_input_fragment() misinterprets them as a ridiculously
    large record size. This in turn causes the fill_input_buffer() routine
    to try reading a ridiculously large amount of data from the network.
    This is why the service stays wedged until you disconnect.
    
    The patch I made to fix this is as follows:
    
    *** xdr_rec.c.orig      Fri May 15 17:43:57 1998
    --- xdr_rec.c   Fri May 15 17:47:58 1998
    ***************
    *** 550,555 ****
    --- 550,561 ----
                    return (FALSE);
            header = (long)ntohl(header);
            rstrm->last_frag = ((header & LAST_FRAG) == 0) ? FALSE : TRUE;
    +       /*
    +        * Sanity check. Try not to accept wildly incorrect
    +        * record sizes.
    +        */
    +       if ((header & (~LAST_FRAG)) > rstrm->recvsize)
    +               return(FALSE);
            rstrm->fbtbc = header & (~LAST_FRAG);
            return (TRUE);
      }
    
    
    The next change relates to the svc_tcp.c module directly. The
    svctcp_recv() routine calls xdr_callmsg() to attempt to decode the RPC
    message header that should accompany every RPC request. With the UDP
    transport, a datagram that doesn't contain a valid header is dropped on
    the floor. With TCP, the connection is left open to attempt to receive
    another request that may be pending. In my view, if no valid message
    header is found where there should have been one, the connection should be
    dropped. The following patch to src/lib/libc/rpc/svc_tcp.c does this:
    
    *** svc_tcp.c.orig      Fri May 15 17:11:21 1998
    --- svc_tcp.c   Fri May 15 17:09:02 1998
    ***************
    *** 404,409 ****
    --- 404,410 ----
                    cd->x_id = msg->rm_xid;
                    return (TRUE);
            }
    +       cd->strm_stat = XPRT_DIED;      /* XXXX */
            return (FALSE);
      }
    
    
    This marks the transport handle as dead if xdr_callmsg() fails, which
    in turn will cause the dispatcher to drop the connection.
    
    With these patches, you have 35 seconds to supply a valid record
    containing an RPC message header and request, otherwise the session
    is disconnected. If you enter garbage data, the connection is dropped
    immediately.
    
    As far as I know, this bug is likely present in all Sun-derived ONC RPC
    implementations, including TI-RPC from ONC+, which is what you'll find in
    Solaris 2.x and I think AIX 4.2 and up. TI-RPC uses the same XDR record
    marking code, although it has an svc_vc.c module to handle virtual circuit
    transports as opposed to a transport-specific svc_tcp.c module. Mind you,
    this observation is based on the TI-RPC 2.3 source, which is quite old.
    
    I do not consider the bug completely fixed though. These patches
    only work around the immediate problem. A proper fix would allow the
    service to continue to handle new requests even while waiting for the 35
    second timeout to expire, and would apply a more intelligent sanity check
    in set_input_fragment(). I think one solution would be to modify readtcp()
    so that it monitors the other transport handles in addition to the
    current socket that it's reading from, but I still have to do some tests
    to see if this idea is really practical.
    
    -Bill
    
    --
    =============================================================================
    -Bill Paul            (212) 854-6020 | System Manager, Master of Unix-Fu
    Work:         wpaulat_private | Center for Telecommunications Research
    Home:  wpaulat_private | Columbia University, New York City
    =============================================================================
      "Now, that's "Open" as used in the sentence "Open your wallet", right?"
    =============================================================================
    



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