Bug 852282 (CVE-2012-3541)

Summary: CVE-2012-3541 rpcbind: -h fails to control access to rpcbind
Product: [Other] Security Response Reporter: Kurt Seifried <kseifried>
Component: vulnerabilityAssignee: Red Hat Product Security <security-response-team>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: carnil, jrusnack, kseifried, mjc, security-response-team, steved
Target Milestone: ---Keywords: Reopened, Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2015-02-24 06:17:11 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Bug Depends On: 1101882, 1101883, 1101884, 1101885    
Bug Blocks: 852284    

Description Kurt Seifried 2012-08-28 06:30:32 UTC
Kurt Seifried (kseifried@redhat.com) of Red Hat reports:

rpcbind offers a "-h" option that binds rpcbind to a specific IP address. If 
the "-h" option is not used then rpcbind will attach to INADDR_ANY 
(0.0.0.0 for ipv4, ::: for ipv6).

When "rpcbind -h 127.0.0.1" is specified rpcbind still attaches to all IP 
addresses: 

# rpcbind -h 127.0.0.1
# ps xauww | grep rpcbind
root 24618 0.0 0.0 19172   848 ? Ss 00:19 0:00 rpcbind -h 127.0.0.1

# netstat -vatn | grep 111
tcp 0 0 0.0.0.0:111   0.0.0.0:* LISTEN      
tcp 0 0 :::111        :::*      LISTEN      

However if you check using nmap:

# nmap -sU -p 111 192.168.1.2

Starting Nmap 5.51 ( http://nmap.org ) at 2012-08-28 00:19 MDT
Nmap scan report for 192.168.1.2
Host is up (0.00045s latency).
PORT    STATE  SERVICE
111/udp closed rpcbind
MAC Address: 00:00:45:67:89:AB (Cadmus Computer Systems)

Nmap done: 1 IP address (1 host up) scanned in 0.39 seconds

So this is good right? Except if you run rpcinfo:

[root@fw00 ~]# rpcinfo  192.168.1.2
   program version netid     address                service    owner
    100000    4    tcp6      ::.0.111               portmapper superuser
    100000    3    tcp6      ::.0.111               portmapper superuser
    100000    4    udp6      ::1.0.111              portmapper superuser
    100000    3    udp6      ::1.0.111              portmapper superuser
    100000    4    tcp       0.0.0.0.0.111          portmapper superuser
    100000    3    tcp       0.0.0.0.0.111          portmapper superuser
    100000    2    tcp       0.0.0.0.0.111          portmapper superuser
    100000    4    udp       127.0.0.1.0.111        portmapper superuser
    100000    3    udp       127.0.0.1.0.111        portmapper superuser
    100000    2    udp       127.0.0.1.0.111        portmapper superuser
    100000    4    local     /var/run/rpcbind.sock  portmapper superuser
    100000    3    local     /var/run/rpcbind.sock  portmapper superuser

Which results in a disclosure of information. Tested on RHEL 6, Fedora and 
Debian 6 (all of which include ship 0.2.0 which was released in 2009).

As an interim workaround ensure that port 111 is firewalled properly in 
iptables and ip6tables.

Comment 1 Vincent Danen 2012-11-21 20:49:59 UTC
This also happens with rpc.statd's -n option, which means that it may affect all of the programs in nfs-utils (shared code).  For instance:

[root@thor sysconfig]# fg
rpc.statd -p 662 -o 2020 -n localhost -d -F
^Z
[1]+  Stopped                 rpc.statd -p 662 -o 2020 -n localhost -d -F
[root@thor sysconfig]# netstat -l --tcp -p|grep statd
tcp        0      0 *:pftp                      *:*                         LISTEN      2251/rpc.statd      
tcp        0      0 *:pftp                      *:*                         LISTEN      2251/rpc.statd

Comment 2 Vincent Danen 2012-11-21 20:53:59 UTC
Actually, the above would be from nfs-utils while rpcbind is from the rpcbind package.  It might be shared code, but maybe not (I thought rpcbind was still from nfs-utils, but it isn't, at least on RHEL6).

Comment 3 Vincent Danen 2012-11-21 20:55:39 UTC
Also forgot to note the Debian bug that brought rpc.statd to my attention:

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=457095

Comment 7 Tomas Hoger 2015-02-03 13:00:34 UTC
(In reply to Kurt Seifried from comment #0)
> rpcbind offers a "-h" option that binds rpcbind to a specific IP address. If 
> the "-h" option is not used then rpcbind will attach to INADDR_ANY 
> (0.0.0.0 for ipv4, ::: for ipv6).

Quoting documentation for this option from the rpcbind(8) man page:

  -h

  Specify specific IP addresses to bind to for *UDP* requests.  This option
  may be specified multiple times and is typically necessary when running
  on a multi-homed host.  If no -h option is specified, rpcbind will bind
  to INADDR_ANY, which could lead to problems on a multi-homed host due to
  rpcbind returning a UDP packet from a different IP address than it was
  sent to.  Note that when specifying IP addresses with -h, rpcbind will
  automatically add 127.0.0.1 and if IPv6 is enabled, ::1 to the list.

Emphasis for UDP is mine.

> When "rpcbind -h 127.0.0.1" is specified rpcbind still attaches to all IP 
> addresses:

...

> # netstat -vatn | grep 111
> tcp 0 0 0.0.0.0:111   0.0.0.0:* LISTEN
> tcp 0 0 :::111        :::*      LISTEN

This only checks TCP sockets, and does not check UDP, which is what should be affected by the use of -h option according to the man page.  This is more complete output, first rpcbind without any -h option:

# netstat -vatun | grep :111
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     
tcp6       0      0 :::111                  :::*                    LISTEN     
udp        0      0 0.0.0.0:111             0.0.0.0:*                          
udp6       0      0 :::111                  :::*

and with -h 127.0.0.1:

# netstat -vatun | grep :111
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN     
tcp6       0      0 :::111                  :::*                    LISTEN     
udp        0      0 127.0.0.1:111           0.0.0.0:*                          
udp6       0      0 ::1:111                 :::*

Use of the -h option makes rpcbind only bind 127.0.0.1/::1 UDP/UDP6.

> However if you check using nmap:
> 
> # nmap -sU -p 111 192.168.1.2
...
> PORT    STATE  SERVICE
> 111/udp closed rpcbind

This tests UDP port, which is no longer bound to INADDR_ANY.  Repeating for both UDP and TCP yields:

# nmap -sT -sU -p 111 192.168.1.2
...
PORT    STATE  SERVICE
111/tcp open   rpcbind
111/udp closed rpcbind

This is consistent with the netstat output above.

> So this is good right? Except if you run rpcinfo:
> 
> [root@fw00 ~]# rpcinfo  192.168.1.2

This uses TCP, which is not restricted by -h.   Compare with the following:

$ rpcinfo -l -T tcp 192.168.1.2 100000 4

vs.

$ rpcinfo -l -T udp 192.168.1.2 100000 4

AFAICS, this works as documented.  It does not seem -h is intended be access control mechanism, but rather a way to prevent failures for UDP and multi-homed hosts.

So this seems to be the way to control network access to the service:

> As an interim workaround ensure that port 111 is firewalled properly in 
> iptables and ip6tables.

Alternative is to use hosts.allow / hosts.deny, as rpcbind uses TCP wrappers.

Ok to close:notabug?

Comment 8 Kurt Seifried 2015-02-03 19:54:14 UTC
So CVE-2012-3541 (https://bugzilla.redhat.com/show_bug.cgi?id=852282) is a somewhat odd case, rpcbind -h controls rpc binding for UDP (but not TCP, that's a whole other discussion). 

If you bind rpcbind to localhost you cannot connect remotely as expected, but locally you still can for example connect to an IP bound to the system (e.g. 10.1.2.3) and connect. So for example:

/usr/sbin/rpcbind -w -h 127.0.0.1


# lsof -P | grep ":111"
      Output information may be incomplete.
rpcbind   16669            rpc    6u     IPv4              57635       0t0        UDP *:111 
rpcbind   16669            rpc    8u     IPv4              57637       0t0        TCP *:111 (LISTEN)
rpcbind   16669            rpc    9u     IPv6              57638       0t0        UDP *:111 
rpcbind   16669            rpc   11u     IPv6              57640       0t0        TCP *:111 (LISTEN)

So the CVE stays as this violates the stated security policy of -h

Comment 9 Tomas Hoger 2015-02-03 21:29:58 UTC
(In reply to Kurt Seifried from comment #8)
> If you bind rpcbind to localhost you cannot connect remotely as expected,
> but locally you still can for example connect to an IP bound to the system
> (e.g. 10.1.2.3) and connect.

Can you hint how to demonstrate that?

> So for example:

...

> # lsof -P | grep ":111"

When running with -h 127.0.0.1, I see:

# lsof -P | grep ":111"
rpcbind   24530     rpc    6u     IPv4     129144      0t0        UDP localhost:111 
rpcbind   24530     rpc   10u     IPv4     129150      0t0        TCP *:111 (LISTEN)
rpcbind   24530     rpc   11u     IPv6     129152      0t0        UDP localhost:111 
rpcbind   24530     rpc   14u     IPv6     129156      0t0        TCP *:111 (LISTEN)

Comment 10 Kurt Seifried 2015-02-24 06:17:11 UTC
So unfortunately I forgot that Linux has the loose IP matching, e.g.
from a local system packets will get delivered that maybe should not be,
that's just how things are. Redid my testing from a remote system and
confirmed I was wrong.

Please REJECT CVE-2012-3541, I have confirmed it behaves as expected,
annoyingly it does filter UDP, but not TCP, as the man page states (this
feature is a whole other discussion). Why the -h option only handles UDP
and not TCP... anyways.