CVE-2017-12993: Out-of-bounds Read (CWE-125)
value: The Juniper protocols parser in tcpdump before 4.9.2 has a buffer over-read in print-juniper.c, several functions.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2017-09-14T06:29Z
Repository: https://github.com/the-tcpdump-group/tcpdump
Language: Other
Files changed: print-juniper.c, tests/TESTLIST, tests/hoobr_juniper.out, tests/hoobr_juniper2.out, tests/hoobr_juniper3.out
Commit date: 2017-09-13 12:25:44 +0100
Commit message: CVE-2017-12993/Juniper: Add more bounds checks.

This fixes a buffer over-read discovered by Kamil Frankowicz.

Add tests using the capture files supplied by the reporter(s).

Diff: diff --git a/print-juniper.c b/print-juniper.c
index 9a258d9ae..e8879ea7b 100644
--- a/print-juniper.c
+++ b/print-juniper.c
@@ -470,10 +470,11 @@ juniper_ggsn_print(netdissect_options *ndo,
             return l2info.header_len;
 
         p+=l2info.header_len;
         gh = (struct juniper_ggsn_header *)&l2info.cookie;
 
+        ND_TCHECK(*gh);
         if (ndo->ndo_eflag) {
             ND_PRINT((ndo, "proto %s (%u), vlan %u: ",
                    tok2str(juniper_protocol_values,"Unknown",gh->proto),
                    gh->proto,
                    EXTRACT_16BITS(&gh->vlan_id[0])));
@@ -490,10 +491,14 @@ juniper_ggsn_print(netdissect_options *ndo,
             if (!ndo->ndo_eflag)
                 ND_PRINT((ndo, "unknown GGSN proto (%u)", gh->proto));
         }
 
         return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_services]"));
+	return l2info.header_len;
 }
 #endif
 
 #ifdef DLT_JUNIPER_ES
 u_int
@@ -517,10 +522,11 @@ juniper_es_print(netdissect_options *ndo,
             return l2info.header_len;
 
         p+=l2info.header_len;
         ih = (const struct juniper_ipsec_header *)p;
 
+        ND_TCHECK(*ih);
         switch (ih->type) {
         case JUNIPER_IPSEC_O_ESP_ENCRYPT_ESP_AUTHEN_TYPE:
         case JUNIPER_IPSEC_O_ESP_ENCRYPT_AH_AUTHEN_TYPE:
             rewrite_len = 0;
             es_type_bundle = 1;
@@ -562,10 +568,14 @@ juniper_es_print(netdissect_options *ndo,
             }
         }
 
         ip_print(ndo, p, l2info.length);
         return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_services]"));
+	return l2info.header_len;
 }
 #endif
 
 #ifdef DLT_JUNIPER_MONITOR
 u_int
@@ -586,20 +596,25 @@ juniper_monitor_print(netdissect_options *ndo,
             return l2info.header_len;
 
         p+=l2info.header_len;
         mh = (const struct juniper_monitor_header *)p;
 
+        ND_TCHECK(*mh);
         if (ndo->ndo_eflag)
             ND_PRINT((ndo, "service-id %u, iif %u, pkt-type %u: ",
                    EXTRACT_32BITS(&mh->service_id),
                    EXTRACT_16BITS(&mh->iif),
                    mh->pkt_type));
 
         /* no proto field - lets guess by first byte of IP header*/
         ip_heuristic_guess (ndo, p, l2info.length);
 
         return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_services]"));
+	return l2info.header_len;
 }
 #endif
 
 #ifdef DLT_JUNIPER_SERVICES
 u_int
@@ -620,10 +635,11 @@ juniper_services_print(netdissect_options *ndo,
             return l2info.header_len;
 
         p+=l2info.header_len;
         sh = (const struct juniper_services_header *)p;
 
+        ND_TCHECK(*sh);
         if (ndo->ndo_eflag)
             ND_PRINT((ndo, "service-id %u flags 0x%02x service-set-id 0x%04x iif %u: ",
                    sh->svc_id,
                    sh->flags_len,
                    EXTRACT_16BITS(&sh->svc_set_id),
@@ -631,10 +647,14 @@ juniper_services_print(netdissect_options *ndo,
 
         /* no proto field - lets guess by first byte of IP header*/
         ip_heuristic_guess (ndo, p, l2info.length);
 
         return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_services]"));
+	return l2info.header_len;
 }
 #endif
 
 #ifdef DLT_JUNIPER_PPPOE
 u_int
@@ -738,10 +758,11 @@ juniper_pppoe_atm_print(netdissect_options *ndo,
         if (juniper_parse_header(ndo, p, h, &l2info) == 0)
             return l2info.header_len;
 
         p+=l2info.header_len;
 
+        ND_TCHECK2(p[0], 2);
         extracted_ethertype = EXTRACT_16BITS(p);
         /* this DLT contains nothing but raw PPPoE frames,
          * prepended with a type field*/
         if (ethertype_print(ndo, extracted_ethertype,
                               p+ETHERTYPE_LEN,
@@ -750,10 +771,14 @@ juniper_pppoe_atm_print(netdissect_options *ndo,
                               NULL, NULL) == 0)
             /* ether_type not known, probably it wasn't one */
             ND_PRINT((ndo, "unknown ethertype 0x%04x", extracted_ethertype));
 
         return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_pppoe_atm]"));
+	return l2info.header_len;
 }
 #endif
 
 #ifdef DLT_JUNIPER_MLPPP
 u_int
@@ -938,10 +963,11 @@ juniper_atm1_print(netdissect_options *ndo,
         if (l2info.cookie[0] == 0x80) { /* OAM cell ? */
             oam_print(ndo, p, l2info.length, ATM_OAM_NOHEC);
             return l2info.header_len;
         }
 
+        ND_TCHECK2(p[0], 3);
         if (EXTRACT_24BITS(p) == 0xfefe03 || /* NLPID encaps ? */
             EXTRACT_24BITS(p) == 0xaaaa03) { /* SNAP encaps ? */
 
             llc_hdrlen = llc_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
             if (llc_hdrlen > 0)
@@ -956,10 +982,14 @@ juniper_atm1_print(netdissect_options *ndo,
 
         if (ip_heuristic_guess(ndo, p, l2info.length) != 0) /* last try - vcmux encaps ? */
             return l2info.header_len;
 
 	return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_atm1]"));
+	return l2info.header_len;
 }
 #endif
 
 /*
  *     ATM2 PIC cookie format
@@ -987,10 +1017,11 @@ juniper_atm2_print(netdissect_options *ndo,
         if (l2info.cookie[7] & ATM2_PKT_TYPE_MASK) { /* OAM cell ? */
             oam_print(ndo, p, l2info.length, ATM_OAM_NOHEC);
             return l2info.header_len;
         }
 
+        ND_TCHECK2(p[0], 3);
         if (EXTRACT_24BITS(p) == 0xfefe03 || /* NLPID encaps ? */
             EXTRACT_24BITS(p) == 0xaaaa03) { /* SNAP encaps ? */
 
             llc_hdrlen = llc_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
             if (llc_hdrlen > 0)
@@ -1014,10 +1045,14 @@ juniper_atm2_print(netdissect_options *ndo,
 
         if (ip_heuristic_guess(ndo, p, l2info.length) != 0) /* last try - vcmux encaps ? */
             return l2info.header_len;
 
 	return l2info.header_len;
+
+trunc:
+	ND_PRINT((ndo, "[|juniper_atm2]"));
+	return l2info.header_len;
 }
 #endif
 
 
 /* try to guess, based on all PPP protos that are supported in
@@ -1278,10 +1313,11 @@ juniper_parse_header(netdissect_options *ndo,
     p+=l2info->header_len;
     l2info->length -= l2info->header_len;
     l2info->caplen -= l2info->header_len;
 
     /* search through the cookie table and copy values matching for our PIC type */
+    ND_TCHECK(p[0]);
     while (lp->s != NULL) {
         if (lp->pictype == l2info->pictype) {
 
             l2info->cookie_len += lp->cookie_len;
 
@@ -1358,10 +1394,11 @@ juniper_parse_header(netdissect_options *ndo,
 #endif
 #ifdef DLT_JUNIPER_MLFR
     case DLT_JUNIPER_MLFR:
         switch (l2info->cookie_type) {
         case LS_COOKIE_ID:
+            ND_TCHECK2(p[0], 2);
             l2info->bundle = l2info->cookie[1];
             l2info->proto = EXTRACT_16BITS(p);
             l2info->header_len += 2;
             l2info->length -= 2;
             l2info->caplen -= 2;
@@ -1381,10 +1418,11 @@ juniper_parse_header(netdissect_options *ndo,
 #endif
 #ifdef DLT_JUNIPER_MFR
     case DLT_JUNIPER_MFR:
         switch (l2info->cookie_type) {
         case LS_COOKIE_ID:
+            ND_TCHECK2(p[0], 2);
             l2info->bundle = l2info->cookie[1];
             l2info->proto = EXTRACT_16BITS(p);
             l2info->header_len += 2;
             l2info->length -= 2;
             l2info->caplen -= 2;
diff --git a/tests/TESTLIST b/tests/TESTLIST
index 5376c12a8..f14868fc4 100644
--- a/tests/TESTLIST
+++ b/tests/TESTLIST
@@ -449,10 +449,13 @@ isakmp-rfc3948-oobr	isakmp-rfc3948-oobr.pcap	isakmp-rfc3948-oobr.out
 isoclns-oobr		isoclns-oobr.pcap		isoclns-oobr.out
 nfs-attr-oobr		nfs-attr-oobr.pcap		nfs-attr-oobr.out
 decnet-oobr		decnet-oobr.pcap		decnet-oobr.out
 oobr_parse_elements	oobr_parse_elements.pcap	oobr_parse_elements.out
 hoobr_ripng_print	hoobr_ripng_print.pcap		hoobr_ripng_print.out
+hoobr_juniper		hoobr_juniper.pcap		hoobr_juniper.out
+hoobr_juniper2		hoobr_juniper2.pcap		hoobr_juniper2.out
+hoobr_juniper3		hoobr_juniper3.pcap		hoobr_juniper3.out
 
 # bad packets from Wilfried Kirsch
 slip-bad-direction	slip-bad-direction.pcap		slip-bad-direction.out	-ve
 
 # bad packets from Otto Airamo and Antti Levomäki
diff --git a/tests/hoobr_juniper.out b/tests/hoobr_juniper.out
new file mode 100644
index 000000000..b13cfbee0
--- /dev/null
+++ b/tests/hoobr_juniper.out
@@ -0,0 +1 @@
+[|juniper_hdr], length 808464432
diff --git a/tests/hoobr_juniper.pcap b/tests/hoobr_juniper.pcap
new file mode 100644
index 000000000..831a8c7db
Binary files /dev/null and b/tests/hoobr_juniper.pcap differ
diff --git a/tests/hoobr_juniper2.out b/tests/hoobr_juniper2.out
new file mode 100644
index 000000000..1f164cfba
--- /dev/null
+++ b/tests/hoobr_juniper2.out
@@ -0,0 +1 @@
+[|juniper_atm1]
diff --git a/tests/hoobr_juniper2.pcap b/tests/hoobr_juniper2.pcap
new file mode 100644
index 000000000..7c7673591
Binary files /dev/null and b/tests/hoobr_juniper2.pcap differ
diff --git a/tests/hoobr_juniper3.out b/tests/hoobr_juniper3.out
new file mode 100644
index 000000000..8062118b5
--- /dev/null
+++ b/tests/hoobr_juniper3.out
@@ -0,0 +1 @@
+Unknown Juniper DLT_ type 132: [|juniper_services]
diff --git a/tests/hoobr_juniper3.pcap b/tests/hoobr_juniper3.pcap
new file mode 100644
index 000000000..2ab9c6630
Binary files /dev/null and b/tests/hoobr_juniper3.pcap differ

Fixed code:         ND_TCHECK(*gh);

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
        ND_TCHECK(*ih);

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
        ND_TCHECK(*mh);

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
        ND_TCHECK(*sh);

trunc:
	ND_PRINT((ndo, "[|juniper_services]"));
	return l2info.header_len;
        ND_TCHECK2(p[0], 2);

trunc:
	ND_PRINT((ndo, "[|juniper_pppoe_atm]"));
	return l2info.header_len;
        ND_TCHECK2(p[0], 3);

trunc:
	ND_PRINT((ndo, "[|juniper_atm1]"));
	return l2info.header_len;
        ND_TCHECK2(p[0], 3);

trunc:
	ND_PRINT((ndo, "[|juniper_atm2]"));
	return l2info.header_len;
    ND_TCHECK(p[0]);
            ND_TCHECK2(p[0], 2);
            ND_TCHECK2(p[0], 2);
hoobr_juniper		hoobr_juniper.pcap		hoobr_juniper.out
hoobr_juniper2		hoobr_juniper2.pcap		hoobr_juniper2.out
hoobr_juniper3		hoobr_juniper3.pcap		hoobr_juniper3.out
[|juniper_hdr], length 808464432
[|juniper_atm1]
Unknown Juniper DLT_ type 132: [|juniper_services]