[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] CVE-2016-4049: Denial of Service Vulnerability in Quagga BGP Routing Daemon (bgpd)
From:       Evgeny Uskov <eu () qrator ! net>
Date:       2016-04-27 15:34:09
Message-ID: 5720DBF1.4010200 () qrator ! net
[Download RAW message or body]

Hello,

About 3 months ago we found the following vulnerability in BGP daemon
from Quagga routing software (bgpd): if the following conditions are
satisfied:
 - regular dumping is enabled
 - bgpd instance has many BGP peers
then BGP message packets that are big enough cause bgpd to crash.
The situation when the conditions above are satisfied is quite common.
Moreover, it is easy to craft a packet which is much "bigger" than a
typical packet, and hence such crafted packet can much more likely cause
the crash.

The reason of such behavior is as follows. The function
bgp_dump_routes_func in bgpd/bgp_dump.c does not perform any size checks
when writing data to bgp_dump_obuf. For each bgp_node table record it
tries to dump all data to bgp_dump_obuf stream which is of limited size.
If there is no free space in this stream, the assertion fails and bgpd
crashes.

The problem seems to be quite serious since it may occur if bgpd has
many BGP peers announcing the same prefix (e.g. if bgpd is used as BGP
reflector, on Internet Exchanges etc), and regular dumping is enabled.
In our case "many" was equal to 20.

The easiest way to reproduce the problem:
1) add 150 BGP neighbors announcing the same prefix
2) write "dump bgp routes-mrt bview.dat" command to the telnet console.

The easiest way to eliminate the problem is to create multiple MRT
records if there is too much data for a prefix. Please see the attached
file dump_fix.patch implementing such solution.

We contacted Quagga developers and sent them patches of this
vulnerability. They responded that they are going to apply these patches
in the next patching round:
 - https://lists.quagga.net/pipermail/quagga-dev/2016-January/014699.html=

 - https://lists.quagga.net/pipermail/quagga-dev/2016-February/014743.htm=
l
However, the vulnerability is still not patched and it is unclear how
long to wait.

This issue has been assigned the name CVE-2016-4049.

--
| Evgeny Uskov  | HLL l QRATOR
| mob.: +7 916 319 33 20
| skype: evgeny_uskov
| mailto: eu@qrator.net
| visit: www.qrator.net


["dump_fix.patch" (text/x-patch)]

From 55604750d694119a0f10a2549dcbdc3d10b7bf1b Mon Sep 17 00:00:00 2001
From: Evgeny Uskov <eu@qrator.net>
Date: Wed, 13 Jan 2016 13:58:00 +0300
Subject: [PATCH] bgpd: Fix buffer overflow error in bgp_dump_routes_func

Now if the number of entries for some prefix is too large, multiple TABLE_DUMP_V2 records are created.
In the previous version in such situation bgpd crashed with SIGABRT.
---
 bgpd/bgp_dump.c | 167 +++++++++++++++++++++++++++++++-------------------------
 1 file changed, 94 insertions(+), 73 deletions(-)

diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
index 1fa0e65..7d78815 100644
--- a/bgpd/bgp_dump.c
+++ b/bgpd/bgp_dump.c
@@ -297,11 +297,96 @@ bgp_dump_routes_index_table(struct bgp *bgp)
 }
 
 
+static struct bgp_info *
+bgp_dump_route_node_record (int afi, struct bgp_node *rn, struct bgp_info *info, unsigned int seq)
+{
+  struct stream *obuf;
+  size_t sizep;
+  size_t endp;
+
+  obuf = bgp_dump_obuf;
+  stream_reset(obuf);
+
+  /* MRT header */
+  if (afi == AFI_IP)
+    bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST,
+                     BGP_DUMP_ROUTES);
+  else if (afi == AFI_IP6)
+    bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST,
+                     BGP_DUMP_ROUTES);
+
+  /* Sequence number */
+  stream_putl(obuf, seq);
+
+  /* Prefix length */
+  stream_putc (obuf, rn->p.prefixlen);
+
+  /* Prefix */
+  if (afi == AFI_IP)
+  {
+    /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
+    stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8);
+  }
+  else if (afi == AFI_IP6)
+  {
+    /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
+    stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8);
+  }
+
+  /* Save where we are now, so we can overwride the entry count later */
+  sizep = stream_get_endp(obuf);
+
+  /* Entry count */
+  uint16_t entry_count = 0;
+
+  /* Entry count, note that this is overwritten later */
+  stream_putw(obuf, 0);
+
+  endp = stream_get_endp(obuf);
+  for (; info; info = info->next)
+  {
+    size_t cur_endp;
+
+    /* Peer index */
+    stream_putw(obuf, info->peer->table_dump_index);
+
+    /* Originated */
+#ifdef HAVE_CLOCK_MONOTONIC
+          stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime));
+#else
+    stream_putl (obuf, info->uptime);
+#endif /* HAVE_CLOCK_MONOTONIC */
+
+    /* Dump attribute. */
+    /* Skip prefix & AFI/SAFI for MP_NLRI */
+    bgp_dump_routes_attr (obuf, info->attr, &rn->p);
+
+    cur_endp = stream_get_endp(obuf);
+    if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER
+                   + BGP_DUMP_HEADER_SIZE)
+    {
+      stream_set_endp(obuf, endp);
+      break;
+    }
+
+    entry_count++;
+    endp = cur_endp;
+  }
+
+  /* Overwrite the entry count, now that we know the right number */
+  stream_putw_at (obuf, sizep, entry_count);
+
+  bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
+  fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
+
+  return info;
+}
+
+
 /* Runs under child process. */
 static unsigned int
 bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
 {
-  struct stream *obuf;
   struct bgp_info *info;
   struct bgp_node *rn;
   struct bgp *bgp;
@@ -320,81 +405,17 @@ bgp_dump_routes_func (int afi, int first_run, unsigned int seq)
   if(first_run)
     bgp_dump_routes_index_table(bgp);
 
-  obuf = bgp_dump_obuf;
-  stream_reset(obuf);
-
   /* Walk down each BGP route. */
   table = bgp->rib[afi][SAFI_UNICAST];
 
   for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
     {
-      if(!rn->info)
-        continue;
-
-      stream_reset(obuf);
-
-      /* MRT header */
-      if (afi == AFI_IP)
-	bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST,
-			 BGP_DUMP_ROUTES);
-      else if (afi == AFI_IP6)
-	bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST,
-			 BGP_DUMP_ROUTES);
-
-      /* Sequence number */
-      stream_putl(obuf, seq);
-
-      /* Prefix length */
-      stream_putc (obuf, rn->p.prefixlen);
-
-      /* Prefix */
-      if (afi == AFI_IP)
-        {
-          /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
-          stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8);
-        }
-      else if (afi == AFI_IP6)
-        {
-          /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */
-          stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8);
-        }
-
-      /* Save where we are now, so we can overwride the entry count later */
-      int sizep = stream_get_endp(obuf);
-
-      /* Entry count */
-      uint16_t entry_count = 0;
-
-      /* Entry count, note that this is overwritten later */
-      stream_putw(obuf, 0);
-
-      for (info = rn->info; info; info = info->next)
-        {
-          entry_count++;
-
-          /* Peer index */
-          stream_putw(obuf, info->peer->table_dump_index);
-
-          /* Originated */
-#ifdef HAVE_CLOCK_MONOTONIC
-          stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime));
-#else
-          stream_putl (obuf, info->uptime);
-#endif /* HAVE_CLOCK_MONOTONIC */
-
-          /* Dump attribute. */
-          /* Skip prefix & AFI/SAFI for MP_NLRI */
-          bgp_dump_routes_attr (obuf, info->attr, &rn->p);
-        }
-
-      /* Overwrite the entry count, now that we know the right number */
-      stream_putw_at (obuf, sizep, entry_count);
-
-      seq++;
-
-      bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
-      fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp);
-
+      info = rn->info;
+      while (info)
+      {
+        info = bgp_dump_route_node_record(afi, rn, info, seq);
+        seq++;
+      }
     }
 
   fflush (bgp_dump_routes.fp);
@@ -841,8 +862,8 @@ bgp_dump_init (void)
   memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump));
   memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump));
 
-  bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER
-                              + BGP_DUMP_HEADER_SIZE);
+  bgp_dump_obuf = stream_new ((BGP_MAX_PACKET_SIZE << 1)
+                              + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE);
 
   install_node (&bgp_dump_node, config_write_bgp_dump);
 
-- 
2.8.0.rc3





[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic