CVE-2017-12985: Out-of-bounds Read (CWE-125)
value: The IPv6 parser in tcpdump before 4.9.2 has a buffer over-read in print-ip6.c:ip6_print().
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-ip6.c, print-mobility.c, tests/TESTLIST, tests/ipv6-next-header-oobr-1.out, tests/ipv6-next-header-oobr-2.out
Commit date: 2017-09-13 12:25:44 +0100
Commit message: CVE-2017-12985/IPv6: Check for print routines returning -1 when running past the end.

rt6_print(), ah_print(), and esp_print() return -1 if they run up
against the end of the packet while dissecting; if that happens, stop
dissecting, don't try to fetch the next header value, because 1) *it*
might be past the end of the packet and 2) we won't be using it in any
case, as we'll be exiting the loop.

Also, change mobility_print() to return -1 if it runs up against the
end of the packet, and stop dissecting if it does so.

This fixes a buffer over-read discovered by Brian 'geeknik' Carpenter.

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

Diff: diff --git a/print-ip6.c b/print-ip6.c
index 0075fcea2..f2f56efb5 100644
--- a/print-ip6.c
+++ b/print-ip6.c
@@ -278,10 +278,12 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
 
 	cp = (const u_char *)ip6;
 	advance = sizeof(struct ip6_hdr);
 	nh = ip6->ip6_nxt;
 	while (cp < ndo->ndo_snapend && advance > 0) {
+		if (len < (u_int)advance)
+			goto trunc;
 		cp += advance;
 		len -= advance;
 
 		if (cp == (const u_char *)(ip6 + 1) &&
 		    nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
@@ -320,14 +322,19 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
 			 * the possiblity of a future extension in
 			 * which payload can be piggybacked atop a
 			 * mobility header.
 			 */
 			advance = mobility_print(ndo, cp, (const u_char *)ip6);
+			if (advance < 0)
+				return;
 			nh = *cp;
 			return;
 		case IPPROTO_ROUTING:
+			ND_TCHECK(*cp);
 			advance = rt6_print(ndo, cp, (const u_char *)ip6);
+			if (advance < 0)
+				return;
 			nh = *cp;
 			break;
 		case IPPROTO_SCTP:
 			sctp_print(ndo, cp, (const u_char *)ip6, len);
 			return;
@@ -343,16 +350,20 @@ ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
 		case IPPROTO_ICMPV6:
 			icmp6_print(ndo, cp, len, (const u_char *)ip6, fragmented);
 			return;
 		case IPPROTO_AH:
 			advance = ah_print(ndo, cp);
+			if (advance < 0)
+				return;
 			nh = *cp;
 			break;
 		case IPPROTO_ESP:
 		    {
 			int enh, padlen;
 			advance = esp_print(ndo, cp, len, (const u_char *)ip6, &enh, &padlen);
+			if (advance < 0)
+				return;
 			nh = enh & 0xff;
 			len -= padlen;
 			break;
 		    }
 		case IPPROTO_IPCOMP:
diff --git a/print-mobility.c b/print-mobility.c
index c7c03ee93..ca5541330 100644
--- a/print-mobility.c
+++ b/print-mobility.c
@@ -330,7 +330,7 @@ mobility_print(netdissect_options *ndo,
 
 	return(mhlen);
 
  trunc:
 	ND_PRINT((ndo, "%s", tstr));
-	return(mhlen);
+	return(-1);
 }
diff --git a/tests/TESTLIST b/tests/TESTLIST
index c18586f67..9b60df27b 100644
--- a/tests/TESTLIST
+++ b/tests/TESTLIST
@@ -430,10 +430,12 @@ ipv6hdr-heapoverflow	ipv6hdr-heapoverflow.pcap	ipv6hdr-heapoverflow.out
 ipv6hdr-heapoverflow-v	ipv6hdr-heapoverflow.pcap	ipv6hdr-heapoverflow-v.out	-v
 otv-heapoverflow-1	otv-heapoverflow-1.pcap		otv-heapoverflow-1.out		-c10
 otv-heapoverflow-2	otv-heapoverflow-2.pcap		otv-heapoverflow-2.out		-c10
 q933-heapoverflow-2	q933-heapoverflow-2.pcap	q933-heapoverflow-2.out
 atm-heapoverflow	atm-heapoverflow.pcap		atm-heapoverflow.out		-c1 -e
+ipv6-next-header-oobr-1	ipv6-next-header-oobr-1.pcap	ipv6-next-header-oobr-1.out
+ipv6-next-header-oobr-2	ipv6-next-header-oobr-2.pcap	ipv6-next-header-oobr-2.out
 
 # bad packets from Kamil Frankowicz
 snmp-heapoverflow-1	snmp-heapoverflow-1.pcap	snmp-heapoverflow-1.out
 snmp-heapoverflow-2	snmp-heapoverflow-2.pcap	snmp-heapoverflow-2.out
 isoclns-heapoverflow-2	isoclns-heapoverflow-2.pcap	isoclns-heapoverflow-2.out	-e -c1
diff --git a/tests/ipv6-next-header-oobr-1.out b/tests/ipv6-next-header-oobr-1.out
new file mode 100644
index 000000000..74eaf72c5
--- /dev/null
+++ b/tests/ipv6-next-header-oobr-1.out
@@ -0,0 +1 @@
+IP6 3030:3030:3030:3030:3030:3030:3030:3030 > 3030:3030:3030:3030:3030:3030:3030:3030: HBH [|ip6]
diff --git a/tests/ipv6-next-header-oobr-1.pcap b/tests/ipv6-next-header-oobr-1.pcap
new file mode 100644
index 000000000..767350d97
Binary files /dev/null and b/tests/ipv6-next-header-oobr-1.pcap differ
diff --git a/tests/ipv6-next-header-oobr-2.out b/tests/ipv6-next-header-oobr-2.out
new file mode 100644
index 000000000..911d2ae23
--- /dev/null
+++ b/tests/ipv6-next-header-oobr-2.out
@@ -0,0 +1 @@
+IP6 3030:3030:3030:3030:3030:3030:3030:3030 > 3030:3030:3030:3030:3030:3030:3030:3030: HBH [|AH]
diff --git a/tests/ipv6-next-header-oobr-2.pcap b/tests/ipv6-next-header-oobr-2.pcap
new file mode 100644
index 000000000..1be5729b8
Binary files /dev/null and b/tests/ipv6-next-header-oobr-2.pcap differ

Vulnerable code: 	return(mhlen);
Fixed code: 		if (len < (u_int)advance)
			goto trunc;
			if (advance < 0)
				return;
			ND_TCHECK(*cp);
			if (advance < 0)
				return;
			if (advance < 0)
				return;
			if (advance < 0)
				return;
	return(-1);
ipv6-next-header-oobr-1	ipv6-next-header-oobr-1.pcap	ipv6-next-header-oobr-1.out
ipv6-next-header-oobr-2	ipv6-next-header-oobr-2.pcap	ipv6-next-header-oobr-2.out
IP6 3030:3030:3030:3030:3030:3030:3030:3030 > 3030:3030:3030:3030:3030:3030:3030:3030: HBH [|ip6]
IP6 3030:3030:3030:3030:3030:3030:3030:3030 > 3030:3030:3030:3030:3030:3030:3030:3030: HBH [|AH]