Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 22 Feb 2002 02:26:26 -0800
From:      "Crist J. Clark" <cjc@attbi.com>
To:        net@freebsd.org
Subject:   TCP Connections to a Broadcast Address
Message-ID:  <20020222022626.A83807@blossom.cjclark.org>

next in thread | raw e-mail | index | archive | help
BSD-based TCP/IP code have a bug with respect to creating TCP
connections to a broadcast address. This bug can potentially be a
security vulnerability when firewall administrators assume that the
TCP implementation works correctly and does not block broadcast
addresses.


The Standard:

TCP connections are only valid when the destination address is a
unicast address. That is, the destination must not be a multicast or
broadcast address. One place this is clearly specified in the
Standards is RFC 1122 (everyone's very most favorite RFC),

         4.2.3.10  Remote Address Validation

         ...

            A TCP implementation MUST silently discard an incoming SYN
            segment that is addressed to a broadcast or multicast
            address.


The Bug:

Uncorrected BSD-based TCP implementations do not actually check if the
destination IP address is a broadcast address. Rather, the packet's
link layer address is checked. Here is the code from FreeBSD's
tcp_input.c,

                 * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
                 * in_broadcast() should never return true on a received
                 * packet with M_BCAST not set.
                 *
                 * Packets with a multicast source address should also
                 * be discarded.
                 */
                if (m->m_flags & (M_BCAST|M_MCAST))
                        goto drop;
#ifdef INET6
                if (isipv6) {
                        if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ||
                            IN6_IS_ADDR_MULTICAST(&ip6->ip6_src))
                                goto drop;
                } else
#endif
                if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
                    IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
                    ip->ip_src.s_addr == htonl(INADDR_BROADCAST))
                        goto drop;

The comment in the code reveals the reason for the mistake. The
authors assume that no one would accidentally or maliciously break the
rules. One can easily send packets with a unicast link-layer address,
but containing an IP broadcast address. No check is made in the above
code for such a pathological situation.


A Demonstration:

There are many ways to take advantage of this bug. Here is one easy
way that will not interfere with normal operations of the test
network:

	1) On the victim machine add an alias for an unused network,

		  # ifconfig if0 inet 192.0.2.1 alias

	2) On the attack machine, which must be local to the victim on
	   the interface configured in step (1), add a route(8),

		# route add 192.0.2.0/24 <victim's IP>

	3) Now from the attacker, try to establish a TCP connection to
	   the victim's broadcast address on any port that might be
	   listening on the victim,

		# ssh <victim's IP>

The connection should succeed. Another slightly scarier attack, since
it doesn't require any changes to the victim host, is to just change
the attacker's idea of the netmask, making the network "bigger," so
that the broadcast address of the network now looks like a unicast to
the attacker. But remember this might break some things on the
attacker while the network is misconfigured.


The Vulnerability:

This creates a potential security vulnerability. The firewall
administrator may assume that it is not possible to establish TCP
connections to a broadcast address and therefore may not protect it
adequately.

This vulnerability is mitigated by a number of factors:

  - If the firewall follows a more secure
    explicitly-pass-default-deny policy, this probably will not be a
    problem.

  - An attacking or misconfigured host must be local to the victim.

  - The attacking host can only connect to broadcast addresses on the
    local interface.

One issue may exacerbates the problem for ipfw(8) users. The 'me'
destination in an ipfw(8) rule does NOT match the interface's
broadcast address. So,

  deny tcp from any to me via if0

Would not block a TCP connection to the broadcast address on if0.
Using rules like the above on a firewall machine meant to allow
forwarded traffic through the external interface, but not allow direct
connections to the firewall is probably not uncommon, but it creates a
vulnerable configuration.


The Fix:

Adding an in_broadcast() check trivially fixes the problem. Here is a
patch (which fixes the comment too),

Index: src/sys/netinet/tcp_input.c
===================================================================
RCS file: /export/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.146
diff -u -r1.146 tcp_input.c
--- src/sys/netinet/tcp_input.c	4 Jan 2002 17:21:27 -0000	1.146
+++ src/sys/netinet/tcp_input.c	17 Feb 2002 12:54:39 -0000
@@ -798,11 +798,10 @@
 		}
 		/*
 		 * RFC1122 4.2.3.10, p. 104: discard bcast/mcast SYN
-		 * in_broadcast() should never return true on a received
-		 * packet with M_BCAST not set.
- 		 *
- 		 * Packets with a multicast source address should also
- 		 * be discarded.
+		 *
+		 * It is possible for a malicious (or misconfigured)
+		 * attacker to send unicast link-layer packets with a
+		 * broadcast IP address. Use in_broadcast() to find them.
 		 */
 		if (m->m_flags & (M_BCAST|M_MCAST))
 			goto drop;
@@ -815,7 +814,8 @@
 #endif
 		if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
 		    IN_MULTICAST(ntohl(ip->ip_src.s_addr)) ||
-		    ip->ip_src.s_addr == htonl(INADDR_BROADCAST))
+		    ip->ip_src.s_addr == htonl(INADDR_BROADCAST) ||
+		    in_broadcast(ip->ip_dst, m->m_pkthdr.rcvif))
 			goto drop;
 		/*
 		 * SYN appears to be valid; create compressed TCP state


Actions:

I notified security-officer@freebsd.org on Feburary 17th. From
examining NetBSD and OpenBSD source code, they appear to have the same
flaw. security-offier@ at each was notified at the same time and
provided with appropriate patches. I have been unable to actually test
the vulnerabilities on an operational NetBSD or OpenBSD system. I have
not heard anything from either NetBSD or OpenBSD, and no changes have
been committed to their code.

rwatson requested I post the above patch to net@ for community review
and feedback. So, if no one has any problems with this fix, I will
commit it. I have approval to add the fix to the RELENG_4_? security
branches once the final fix has been reviewed.

Some other remarks about the checks we see above:

  - The IN_MULTICAST() checks of the source address should not be
    necessary. Multicast source addresses are never valid for IP
    datagrams and should be rejected in the network layer. The same
    applies to the check for multicast source addresses in IPv6.

  - Since IPv6 does not have broadcasts, it should not be vulnerable
    provided the multicast check of the destination address is
    robust. I have not examined the IPv6 code in this respect.

After the fix to FreeBSD is made and other *BSD's have ample time to
responde, I do plan to send a quick note on the subject to Bugtraq.

Thanks for reading. I look forward to comments on the fix.
-- 
Crist J. Clark                     |     cjclark@alum.mit.edu
                                   |     cjclark@jhu.edu
http://people.freebsd.org/~cjc/    |     cjc@freebsd.org

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-net" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020222022626.A83807>