CVE-2017-13022: Out-of-bounds Read (CWE-125)
value: The IP parser in tcpdump before 4.9.2 has a buffer over-read in print-ip.c:ip_printroute().
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-ip.c, tests/TESTLIST, tests/ip_printroute_asan.out
Commit date: 2017-09-13 12:25:44 +0100
Commit message: CVE-2017-13022/IP: Add bounds checks to ip_printroute().

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s), modified
so the capture file won't be rejected as an invalid capture.

Diff: diff --git a/print-ip.c b/print-ip.c
index ccf61a3fcb..6831b7aa2d 100644
--- a/print-ip.c
+++ b/print-ip.c
@@ -52,32 +52,38 @@ static const struct tok ip_option_values[] = {
 };
 
 /*
  * print the recorded route in an IP RR, LSRR or SSRR option.
  */
-static void
+static int
 ip_printroute(netdissect_options *ndo,
               register const u_char *cp, u_int length)
 {
 	register u_int ptr;
 	register u_int len;
 
 	if (length < 3) {
 		ND_PRINT((ndo, " [bad length %u]", length));
-		return;
+		return (0);
 	}
 	if ((length + 1) & 3)
 		ND_PRINT((ndo, " [bad length %u]", length));
+	ND_TCHECK(cp[2]);
 	ptr = cp[2] - 1;
 	if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
 		ND_PRINT((ndo, " [bad ptr %u]", cp[2]));
 
 	for (len = 3; len < length; len += 4) {
+		ND_TCHECK2(cp[len], 4);
 		ND_PRINT((ndo, " %s", ipaddr_string(ndo, &cp[len])));
 		if (ptr > len)
 			ND_PRINT((ndo, ","));
 	}
+	return (0);
+
+trunc:
+	return (-1);
 }
 
 /*
  * If source-routing is present and valid, return the final destination.
  * Otherwise, return IP destination.
@@ -276,11 +282,12 @@ ip_optprint(netdissect_options *ndo,
 			break;
 
 		case IPOPT_RR:       /* fall through */
 		case IPOPT_SSRR:
 		case IPOPT_LSRR:
-			ip_printroute(ndo, cp, option_len);
+			if (ip_printroute(ndo, cp, option_len) == -1)
+				goto trunc;
 			break;
 
 		case IPOPT_RA:
 			if (option_len < 4) {
 				ND_PRINT((ndo, " [bad length %u]", option_len));
diff --git a/tests/TESTLIST b/tests/TESTLIST
index 070010c766..657196ba23 100644
--- a/tests/TESTLIST
+++ b/tests/TESTLIST
@@ -521,10 +521,11 @@ esis_snpa_asan-5	esis_snpa_asan-5.pcap		esis_snpa_asan-5.out	-v
 dhcp6_reconf_asan	dhcp6_reconf_asan.pcap		dhcp6_reconf_asan.out	-v
 pgm_opts_asan		pgm_opts_asan.pcap		pgm_opts_asan.out	-v
 pgm_opts_asan_2		pgm_opts_asan_2.pcap		pgm_opts_asan_2.out	-v
 vtp_asan		vtp_asan.pcap			vtp_asan.out	-v
 icmp6_mobileprefix_asan	icmp6_mobileprefix_asan.pcap	icmp6_mobileprefix_asan.out	-v
+ip_printroute_asan	ip_printroute_asan.pcap		ip_printroute_asan.out	-v
 
 # RTP tests
 # fuzzed pcap
 rtp-seg-fault-1  rtp-seg-fault-1.pcap  rtp-seg-fault-1.out  -v -T rtp
 rtp-seg-fault-2  rtp-seg-fault-2.pcap  rtp-seg-fault-2.out  -v -T rtp
diff --git a/tests/ip_printroute_asan.out b/tests/ip_printroute_asan.out
new file mode 100644
index 0000000000..334973e501
--- /dev/null
+++ b/tests/ip_printroute_asan.out
@@ -0,0 +1,2 @@
+IP (tos 0x0, ttl 254, id 25615, offset 65480, flags [DF, rsvd], proto UDP (17), length 32768, options (LSRR [bad length 25] [bad ptr 15] 103.103.103.0, 0.172.0.116, 0.16.36.36, 16.0.36.2 14.9.36.4[|ip]))
+    251.73.86.0 > 0.172.128.5: ip-proto-17
diff --git a/tests/ip_printroute_asan.pcap b/tests/ip_printroute_asan.pcap
new file mode 100644
index 0000000000..379cd946af
Binary files /dev/null and b/tests/ip_printroute_asan.pcap differ

Vulnerable code: static void
		return;
			ip_printroute(ndo, cp, option_len);
Fixed code: static int
		return (0);
	ND_TCHECK(cp[2]);
		ND_TCHECK2(cp[len], 4);
	return (0);

trunc:
	return (-1);
			if (ip_printroute(ndo, cp, option_len) == -1)
				goto trunc;
ip_printroute_asan	ip_printroute_asan.pcap		ip_printroute_asan.out	-v
IP (tos 0x0, ttl 254, id 25615, offset 65480, flags [DF, rsvd], proto UDP (17), length 32768, options (LSRR [bad length 25] [bad ptr 15] 103.103.103.0, 0.172.0.116, 0.16.36.36, 16.0.36.2 14.9.36.4[|ip]))
    251.73.86.0 > 0.172.128.5: ip-proto-17