Re: (spoofed) RPC portmapper set/unset

From: Theo de Raadt (deraadtat_private)
Date: Sat Nov 14 1998 - 12:13:01 PST

  • Next message: Kevin Vajk: "Re: Xinetd /tmp race?"

    Regarding:
    
    > -pmap_set : it is called when a rpc program wants to register itself in the
    > portmapper list (rpcinfo -p returns this list).
    >
    > -pmap_unset : same as above but it's used to unregister a rpc program.
    > Again, Wietse' portmapper fixed almost all the holes related to pset/punset
    > rpc calls.
    >
    > However, due to a restriction in the protocol, all the security problems
    > cannot be fixed easily. When a rpc program (such as rpc.mountd) wants to
    > un/register itself on the portmapper list, it sends an udp || tcp packet to
    > the portmapper (port 111) using the pmap_set or pmap_unset respectively.
    > The portmapper checks the validity of the call by determining if the rpc
    > packet comes from the localhost using a priviledged source port (between
    > 512 and 1024 when -DCHECK_PORT option is used while compiling portmapper).
    > Unix authentification is not checked.
    
    The unfortunate problem being that the typical Sun-based rpc library
    uses a function called get_myaddress() to determine where it should
    send the packet.  This function returns the machine's local IP
    address, when it really should return 127.0.0.1.  This then causes
    problems because a specially hacked portmap still cannot differentiate
    between a spoofed or non-spoofed request.
    
    > As you can see, it's not big deal. By spoofing a pmap_set/pmap_unset udp
    > packet, it's possible to register or unregister any RPC programs on the
    > remote host (the program I attached to this mail illustrates this). All in
    > all, this can only lead to a DoS on the server for a _remote_ attacker (by
    > flooding the portmapper with pmap_set request or by unregistering services
    > such as mountd, nfsd or ypserv) but it can be worse because a _local_
    > attacker can set up rogue rpc programs on the server (registering his own
    > ypbind/ypserv program for example). This last local attack can lead to root
    > compromise (including the hosts which trust the ypserver); but well, it's
    > fairly clear that when an attacker cracked into one of your server, all
    > your systems are almost already compromised.
    
    However, if we can tell for sure that a request is coming from
    127.0.0.1, and that it is from a reserved port, then we will be doing
    the right thing.
    
    And that is what I did to the OpenBSD portmap and our libc, a long
    time ago:
    
    RCS file: /cvs/src/usr.sbin/portmap/portmap.c,v
    revision 1.3
    date: 1996/06/29 19:03:50;  author: deraadt;  state: Exp;  lines: +135 -64
    multiple receivers, port checking. testing help from bitblt
    
    As you can see, this isn't a new issue, since it was fixed in OpenBSD
    more than two years ago.  There was also a whole bunch of bugtraq
    postings which talked about this, or rather 'talked around this'... a
    very vague sort of thing that happened back when der mouse stupidly
    started spreading information I had asked him to keep private.  Other
    vendor systems (especially statically linked programs) cannot fix this
    as easily since it requires a change to get_myaddress() in libc.
    
    > possible solutions :
    >
    >    - compile your portmapper with -DLOOPBACK_SETUNSET flag.. notice that
    > it's damn hard to implement because you have to change other things in your
    > rpc services as well as in your kernel config.
    
    That functionality in Wietse's portmap appears to be based on the work
    that we did earlier on in OpenBSD.  And as I've said, proper operation
    requires a couple other changes to libc.  You can tell if a portmap
    supports this by noting that port 111 is multiply bound:
    
    tcp        0      0  127.0.0.1.111          *.*                    LISTEN
    tcp        0      0  *.111                  *.*                    LISTEN
    udp        0      0  127.0.0.1.111          *.*
    udp        0      0  *.111                  *.*
    
    Other portmappers may do this differently by using socket options which
    return the address of the sender.
    
    > in libc-5.3.12 code, we can see that the xid of an rpc message is not
    > totally random :
    >
    > Mithrandir:/tmp/libsrc/libc/rpc# grep call_msg.rm_xid clnt* -n
    > clnt_tcp.c:207: call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
    > clnt_udp.c:176: call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
    >
    > getpid is usually not a high value so higher bits of the xid are defined by
    > the now.tv_usec value. An attacker may easily retrieve the date of the
    > system (ie. port 13) so, with a lot of luck and time, he should be able to
    > guess the next xid (ypbind uses a timeout of 10sec I think). Anyway, this
    > is pure theory and I haven't tried it yet so xid prediction may not be
    > easily done but, guess what, crackers are usually lucky and they have
    > plenty of time to spend on their computers...
    
    Yes, a second problem.  This problem was also fixed almost two years
    ago in OpenBSD; and I've also told many people about it in the past
    (hi Oliver!)  I think that an attack on this was very possible against
    /etc/rc-started services which have a known pid (of course in OpenBSD
    pid numbers are now random too).  For instance, on a typically
    compiled SunOS box... ypbind is almost always around pid 666...
    
    clnt_udp.c:     call_msg.rm_xid = arc4random();
    clnt_tcp.c:     call_msg.rm_xid = arc4random();
    pmap_rmt.c:     msg.rm_xid = xid = arc4random();
    
    RCS file: /cvs/src/lib/libc/rpc/clnt_udp.c,v
    revision 1.10
    date: 1997/01/02 09:21:05;  author: deraadt;  state: Exp;  lines: +2 -6
    use arc4random for xid generation
    
    
    Hmm.. These problems don't seem nearly as fresh & new when someone had
    them fixed (in freely available source) two years ago, do they?
    



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