[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

kernel/1490: security related problem in arp handling code.




>Number:         1490
>Category:       kernel
>Synopsis:       hardware address may be entered by packet from wrong interface
>Confidential:   yes
>Severity:       serious
>Priority:       high
>Responsible:    bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Nov  5 17:00:02 MST 2000
>Last-Modified:
>Originator:     Grigoriy Orlov
>Organization:
>Release:        2.8-current
>Environment:
	System      : OpenBSD 2.8-current 20.10.2000
	Architecture: OpenBSD.i386
	Machine     : i686
>Description:

   It is possible to enter hardware address into arp table for IP on one 
interface by packet coming from another interface. 

      -----          -----          -----
      | A |----------| R |----------| H |
      -----          -----          -----

A - attacker host
R - router or firewall with OpenBSD.
H - host for which IP we trying enter hardware address.
ip(H) - H's ip address.

Necessary conditions:
 - machine H must be down or not talk arp protocol.
 - arp table of R not contain H's address.

Scenario:
  Attacker send ip packet to ip(H). R don't know hardware address of H and 
trying resolv it. R allocate 'rtentry'(without hardware address) for ip(H)
in routing table and send arp request.
  Now attacker send arp packet with sender ip=ip(H) and sender hardware 
address filled with value which attacker wish to enter in Router's arp table.

In such scenario all checks in arp code bypassed and router enter hardware
address supplied by attacker for ip(H).

  When H coming up and begin talk arp protocol, then arp entry will be 
overwriten with proper address and attacker will have no chance to change 
it again. But if for example H have statically configured hardware address 
of R, then H will not send arp request to R and remain DoSed. Any ip
packets going through R to H will no reach H, while hardware address not 
overwriten with true value. Probably other nasty tricks are possible.

>How-To-Repeat:

      -----          -----                         -----
      | A |----------| R |-------------------------| H |
      -----          -----                         -----
                         192.168.0.1        192.168.0.2 

1. ping 192.168.0.2 from A when H is down and no entry for 
   192.168.0.2 in R's arp table.
2. set A ip to 192.168.0.2 and ping 192.168.0.1

>Fix:

This patch:
- check arp opcode.
- deny processing and log error for arp reply to broadcast or multicast 
  address in ethernet header (not in arp header!)
  This check prevent very effective arp spoofing attack, when attacker can 
  overwrite arp entries on all machines in the same network only with one 
  broadcast packet. Such attack still possible with request packet.
- add check for hardware multicast source address in arp header.

I think none of these checks conflict with RFC. ARP protocol is complitely 
insecure, but in my opinion some checks may be usefull. 

Last two sections (separated by @@) fix bug described in this PR.

Index: if_ether.c
===================================================================
RCS file: /cvs/src/sys/netinet/if_ether.c,v
retrieving revision 1.22
diff -u -r1.22 if_ether.c
--- if_ether.c	2000/06/22 19:05:51	1.22
+++ if_ether.c	2000/11/05 20:24:38
@@ -460,6 +460,13 @@
 
 	ea = mtod(m, struct ether_arp *);
 	op = ntohs(ea->arp_op);
+	if ((op != ARPOP_REQUEST) && (op != ARPOP_REPLY))
+		goto out;
+	if ((op == ARPOP_REPLY) && (m->m_flags & (M_BCAST|M_MCAST))) {
+		log(LOG_ERR,
+		    "arp: receive reply to broadcast or multicast address\n");
+		goto out;
+	}
 	bcopy((caddr_t)ea->arp_spa, (caddr_t)&isaddr, sizeof (isaddr));
 	bcopy((caddr_t)ea->arp_tpa, (caddr_t)&itaddr, sizeof (itaddr));
 	for (ia = in_ifaddr.tqh_first; ia != 0; ia = ia->ia_list.tqe_next)
@@ -477,11 +484,16 @@
 	if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr,
 	    sizeof (ea->arp_sha)))
 		goto out;	/* it's from me, ignore it. */
-	if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)etherbroadcastaddr,
-	    sizeof (ea->arp_sha))) {
-		log(LOG_ERR,
-		    "arp: ether address is broadcast for IP address %s!\n",
-		    inet_ntoa(isaddr));
+	if (ea->arp_sha[0] & 1) {
+		if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)etherbroadcastaddr,
+		    sizeof (ea->arp_sha)))
+		    log(LOG_ERR,
+			"arp: ether address is broadcast for IP address %s!\n",
+			inet_ntoa(isaddr));
+		else
+		    log(LOG_ERR,
+			"arp: ether address is multicast for IP address %s!\n",
+			inet_ntoa(isaddr));
 		goto out;
 	}
 	if (isaddr.s_addr == myaddr.s_addr) {
@@ -493,8 +505,8 @@
 	}
 	la = arplookup(isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, 0);
 	if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) {
-		if (sdl->sdl_alen &&
-		    bcmp((caddr_t)ea->arp_sha, LLADDR(sdl), sdl->sdl_alen)) {
+		if (sdl->sdl_alen) {
+		    if (bcmp((caddr_t)ea->arp_sha, LLADDR(sdl), sdl->sdl_alen)) {
 		  	if (rt->rt_flags & RTF_PERMANENT_ARP) {
 				log(LOG_WARNING,
 				   "arp: attempt to overwrite permanent "
@@ -519,6 +531,15 @@
 				   (&ac->ac_if)->if_xname);
 				rt->rt_expire = 1; /* no longer static */
 			}
+		    }
+		} else if (rt->rt_ifp != &ac->ac_if) {
+		    log(LOG_WARNING,
+			"arp: attempt to add entry for %s "
+			"on %s by %s on %s\n",
+			inet_ntoa(isaddr), rt->rt_ifp->if_xname,
+			ether_sprintf(ea->arp_sha),
+			(&ac->ac_if)->if_xname);
+		    goto out;
 		}
 		bcopy((caddr_t)ea->arp_sha, LLADDR(sdl),
 		    sdl->sdl_alen = sizeof(ea->arp_sha));

>Audit-Trail:
>Unformatted: