Bugtraq mailing list archives

Re: Solaris rpcbind tricks


From: adam () XPERT COM (Adam Morrison)
Date: Sun, 22 Aug 1999 13:59:45 +0200


  2. Theres no check for the src address and port of the replies to
     forwarded calls to match the dst address and port of the  original
     call.

    rpcbind does not check that RPC reply messages, received on the
    socket used to forward CALLIT requests, have a valid source address,

    port, prognum, progvers, etc.

Heh.  This is actually a generic RPC vulnerability, dating back to the
reference RPC implementation from Sun.

This is the relevant excerpt from clnt_udp.c.  Modern versions of this
part of the code have not fixed this.  (They mostly deal with the ugly
cast at the end.)

                do {
                        fromlen = sizeof(struct sockaddr);
                        inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
                                (int) cu->cu_recvsz, 0,
                                (struct sockaddr *)&from, &fromlen);
                } while (inlen < 0 && errno == EINTR);
                if (inlen < 0) {
                        if (errno == EWOULDBLOCK)
                                continue;
                        cu->cu_error.re_errno = errno;
                        return (cu->cu_error.re_status = RPC_CANTRECV);
                }
                if (inlen < sizeof(u_long))
                        continue;
                /* see if reply transaction id matches sent id */
                if (*((u_long *)(cu->cu_inbuf)) != *((u_long *)(cu->cu_outbuf)))
                        continue;
                /* we now assume we have the proper reply */
                break;

This condition opens a window of vulnerability for an attacker.
Suppose a privileged process makes an RPC query.  Before the server
replies, the attacker can guess the XID and inject a bogus reply back
to the process, with no spoofing involved.

Some examples:

1)  The SunOS NFS client code allocates XIDs from a monotonically
    increasing counter (`clnt_xid').  (In SunOS 4.x, I think the
    counter was initially zero, but that's a hazy recollection.
    In SunOS 5.x, it is initialized upon the first RPC request to
    the number nanoseconds since the system booted.)

    It is therefore possible for a local attacker to e.g. make
    an executable file appear to be setuid root by injecting a
    bogus GETATTR reply.  (But please read my note further on.)

2)  The NIS library utilizes RPC to receive password information.
    Attackers could therefore inject bogus NIS replies to programs
    such as su(1M) to get root.

3)  Similar to the above attack, attackers can poison the passwd
    cache of the Solaris name service cache daemon (nscd(1M)).

So, what's the catch?

To successfully carry out this attack, the attacker has to correctly
guess the RPC xid before the RPC server sends its reply back to the
client.  The xid is typically generated using the oh-so-common method
of

                xid = getpid() ^ now.tv_sec ^ now.tv_usec;

Guessing this under this time constraint proves to be quite difficult.
(It's sometimes difficult to know the value of `tv_sec' exactly,
never mind iterating over `tv_usec'.)  So it does not make for a very
reliable attack.  The rpcbind attack described in the previous message
was unique in this respect; the xid aspect of the attack had to do with
a fake server, so there was no race to win.

Therefore, the general rule of thumb is that if

        - You have a privileged program making an RPC
        - And you can make the server `go away'
        - And you can perform a brute force of the xid within
          the RPC timeout

Then you can do some interesting things; the point being that because
of the failure to check where RPC replies came from, this attack works
well for local, unprivileged attackers.

Wrt to the NFS attack, it is pretty far fetched; I'd imagine that an
environment in which it works would be quite rare.  (It'd require
relatively long timeouts and a VERY quiet client.)

Ob-OpenBSD:  OpenBSD started randomizing RPC xids (in the kernel, too)
in early 1997, when I pointed this out to Theo de Raadt.

Ob-other-BSDs:  The NFS attack I described against SunOS above does not
apply to the U of Guelph NFS implementation, because the code does a
soconnect() to the NFS server.  This causes an implicit check for the
source of returned queries, performed by the socket level.  (But
someone should probably check this again; it's been a while.)


Current thread: