CVE-2017-12895: Out-of-bounds Read (CWE-125)
value: The ICMP parser in tcpdump before 4.9.2 has a buffer over-read in print-icmp.c:icmp_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-icmp.c, tests/TESTLIST, tests/icmp-cksum-oobr-1.out, tests/icmp-cksum-oobr-2.out, tests/icmp-cksum-oobr-3.out, tests/icmp-cksum-oobr-4.out
Commit date: 2017-09-13 12:25:44 +0100
Commit message: CVE-2017-12895/ICMP: Check the availability of data before checksumming it.

This fixes a buffer over-read discovered by Forcepoint's security
researchers Otto Airamo & Antti Levomäki.

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

Diff: diff --git a/print-icmp.c b/print-icmp.c
index 10772213e..ec7525357 100644
--- a/print-icmp.c
+++ b/print-icmp.c
@@ -596,11 +596,12 @@ icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *
              * Check first if the mpls extension header shows a non-zero length.
              * If the length field is not set then silently verify the checksum
              * to check if an extension header is present. This is expedient,
              * however not all implementations set the length field proper.
              */
-            if (!ext_dp->icmp_length) {
+            if (!ext_dp->icmp_length &&
+                ND_TTEST2(ext_dp->icmp_ext_version_res, plen - ICMP_EXTD_MINLEN)) {
                 vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
                 vec[0].len = plen - ICMP_EXTD_MINLEN;
                 if (in_cksum(vec, 1)) {
                     return;
                 }
@@ -617,16 +618,18 @@ icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *
                 ND_PRINT((ndo, " packet not supported"));
                 return;
             }
 
             hlen = plen - ICMP_EXTD_MINLEN;
-            vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
-            vec[0].len = hlen;
-            ND_PRINT((ndo, ", checksum 0x%04x (%scorrect), length %u",
-                   EXTRACT_16BITS(ext_dp->icmp_ext_checksum),
-                   in_cksum(vec, 1) ? "in" : "",
-                   hlen));
+            if (ND_TTEST2(ext_dp->icmp_ext_version_res, hlen)) {
+                vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
+                vec[0].len = hlen;
+                ND_PRINT((ndo, ", checksum 0x%04x (%scorrect), length %u",
+                       EXTRACT_16BITS(ext_dp->icmp_ext_checksum),
+                       in_cksum(vec, 1) ? "in" : "",
+                       hlen));
+            }
 
             hlen -= 4; /* subtract common header size */
             obj_tptr = (const uint8_t *)ext_dp->icmp_ext_data;
 
             while (hlen > sizeof(struct icmp_mpls_ext_object_header_t)) {
diff --git a/tests/TESTLIST b/tests/TESTLIST
index f7e168c6b..c44e2b75f 100644
--- a/tests/TESTLIST
+++ b/tests/TESTLIST
@@ -450,10 +450,14 @@ decnet-oobr		decnet-oobr.pcap		decnet-oobr.out
 slip-bad-direction	slip-bad-direction.pcap		slip-bad-direction.out	-ve
 
 # bad packets from Otto Airamo and Antti Levomäki
 nbns-valgrind		nbns-valgrind.pcap		nbns-valgrind.out	-vvv -e
 arp-oobr		arp-oobr.pcap			arp-oobr.out	-vvv -e
+icmp-cksum-oobr-1	icmp-cksum-oobr-1.pcap		icmp-cksum-oobr-1.out	-vvv -e
+icmp-cksum-oobr-2	icmp-cksum-oobr-2.pcap		icmp-cksum-oobr-2.out	-vvv -e
+icmp-cksum-oobr-3	icmp-cksum-oobr-3.pcap		icmp-cksum-oobr-3.out	-vvv -e
+icmp-cksum-oobr-4	icmp-cksum-oobr-4.pcap		icmp-cksum-oobr-4.out	-vvv -e
 
 # 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/icmp-cksum-oobr-1.out b/tests/icmp-cksum-oobr-1.out
new file mode 100644
index 000000000..2efafee67
--- /dev/null
+++ b/tests/icmp-cksum-oobr-1.out
@@ -0,0 +1,5 @@
+Out 00:16:3e:27:78:a2 ethertype IPv4 (0x0800), length 204: truncated-ip - 13723 bytes missing! (tos 0x72,ECT(0), ttl 64, id 9472, offset 0, flags [none], proto ICMP (1), length 13911, bad cksum 67ea (->8c0c)!)
+    62.220.31.247 > 62.225.245.115: ICMP 62.220.31.247 udp port 1027 unreachable, length 13891
+	(tos 0xa0, ttl 114, id 30054, offset 0, flags [none], proto UDP (17), length 13728, bad cksum 3f1f (->a1f)!)
+    62.225.245.115.9109 > 62.220.31.247.1027: [bad udp cksum 0xdfe7 -> 0xdb95!] UDP, length 132
+	MPLS extension v0 packet not supported
diff --git a/tests/icmp-cksum-oobr-1.pcap b/tests/icmp-cksum-oobr-1.pcap
new file mode 100644
index 000000000..b79f72b1f
Binary files /dev/null and b/tests/icmp-cksum-oobr-1.pcap differ
diff --git a/tests/icmp-cksum-oobr-2.out b/tests/icmp-cksum-oobr-2.out
new file mode 100644
index 000000000..71d6b886d
--- /dev/null
+++ b/tests/icmp-cksum-oobr-2.out
@@ -0,0 +1,11 @@
+IP (0x0021), length 244: truncated-ip - 32768 bytes missing! (tos 0x0, ttl 254, id 59168, offset 0, flags [DF], proto ICMP (1), length 33008, bad cksum 7ade (->fabd)!)
+    10.4.0.34 > 12.4.4.4: ICMP time exceeded in-transit, length 32988
+	(tos 0x0, ttl 1, id 42321, offset 0, flags [none], proto UDP (17), length 40)
+    12.4.4.4.42315 > 12.1.1.1.33440: [bad udp cksum 0x1000 -> 0xbad0!] UDP, length 12
+	MPLS extension v2
+	  Extended Payload Object (2), Class-Type: 14, length 80
+	    0x0000:  0000 000f 0001 0000 0a0a 0a0a 3f54 6869
+	    0x0010:  732d 6973 2d74 6865 2d6e 616d 652d 6f66
+	    0x0020:  2d74 6865 2d49 6e74 6572 6661 6365 2d74
+	    0x0030:  6861 742d 7765 2d61 7265 2d6c 6f6f 6b69
+	    0x0040:  6e67 2d66 6f72 2d5b 3a2d 295d[|icmp]
diff --git a/tests/icmp-cksum-oobr-2.pcap b/tests/icmp-cksum-oobr-2.pcap
new file mode 100644
index 000000000..32db8ac90
Binary files /dev/null and b/tests/icmp-cksum-oobr-2.pcap differ
diff --git a/tests/icmp-cksum-oobr-3.out b/tests/icmp-cksum-oobr-3.out
new file mode 100644
index 000000000..4a16fdd83
--- /dev/null
+++ b/tests/icmp-cksum-oobr-3.out
@@ -0,0 +1,5 @@
+00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 337: truncated-ip - 4096 bytes missing! (tos 0x0, ttl 64, id 30662, offset 0, flags [DF], proto ICMP (1), length 4419, bad cksum cdf9 (->bdf9)!)
+    97.242.24.11 > 97.242.24.11: ICMP 97.242.24.11 udp port 162 unreachable, length 4399
+	(tos 0x0, ttl 128, id 30661, offset 0, flags [DF], proto UDP (17), length 295)
+    97.242.24.11.60377 > 97.242.24.11.162: [udp sum ok]  { SNMPv1 C="trap" { Trap(251)  .1.3.6.1.4.1.3830.1.1.2.2.1 97.242.24.11 enterpriseSpecific s=52 61498489 .1.3.6.1.4.1.3830.1.1.2.1.1.1=3 .1.3.6.1.4.1.3830.1.1.2.1.1.2=2 .1.3.6.1.4.1.3830.1.1.2.1.1.3="%SMSA-E-POLLERR, Polling the SMSC was not successful." .1.3.6.1.4.1.3830.1.1.2.1.1.4="OPCOM" .1.3.6.1.4.1.3830.1.1.2.1.1.5="28-OCT-2010 20:42:14.67" .1.3.6.1.4.1.3830.1.1.2.1.1.6="SMRL51" } } 
+	MPLS extension v0 packet not supported
diff --git a/tests/icmp-cksum-oobr-3.pcap b/tests/icmp-cksum-oobr-3.pcap
new file mode 100644
index 000000000..a92d933f6
Binary files /dev/null and b/tests/icmp-cksum-oobr-3.pcap differ
diff --git a/tests/icmp-cksum-oobr-4.out b/tests/icmp-cksum-oobr-4.out
new file mode 100644
index 000000000..2b1208222
--- /dev/null
+++ b/tests/icmp-cksum-oobr-4.out
@@ -0,0 +1,7 @@
+IP (0x0021), length 172: truncated-ip - 8192 bytes missing! (tos 0xc0, ttl 251, id 5047, offset 0, flags [none], proto ICMP (1), length 8360, bad cksum 7edb (->5edb)!)
+    10.0.12.2 > 10.0.12.1: ICMP time exceeded in-transit, length 8340
+	(tos 0x0, ttl 1, id 2574, offset 0, flags [none], proto UDP (17), length 28)
+    10.0.12.1.49215 > 10.255.255.4.33435: [udp sum ok] UDP, length 0
+	MPLS extension v2
+	  MPLS Stack Entry Object (1), Class-Type: 1, length 8
+	    label 16, exp 0, [S], ttl 1[|icmp]
diff --git a/tests/icmp-cksum-oobr-4.pcap b/tests/icmp-cksum-oobr-4.pcap
new file mode 100644
index 000000000..58f472832
Binary files /dev/null and b/tests/icmp-cksum-oobr-4.pcap differ

Vulnerable code:             if (!ext_dp->icmp_length) {
            vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
            vec[0].len = hlen;
            ND_PRINT((ndo, ", checksum 0x%04x (%scorrect), length %u",
                   EXTRACT_16BITS(ext_dp->icmp_ext_checksum),
                   in_cksum(vec, 1) ? "in" : "",
                   hlen));
Fixed code:             if (!ext_dp->icmp_length &&
                ND_TTEST2(ext_dp->icmp_ext_version_res, plen - ICMP_EXTD_MINLEN)) {
            if (ND_TTEST2(ext_dp->icmp_ext_version_res, hlen)) {
                vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
                vec[0].len = hlen;
                ND_PRINT((ndo, ", checksum 0x%04x (%scorrect), length %u",
                       EXTRACT_16BITS(ext_dp->icmp_ext_checksum),
                       in_cksum(vec, 1) ? "in" : "",
                       hlen));
            }
icmp-cksum-oobr-1	icmp-cksum-oobr-1.pcap		icmp-cksum-oobr-1.out	-vvv -e
icmp-cksum-oobr-2	icmp-cksum-oobr-2.pcap		icmp-cksum-oobr-2.out	-vvv -e
icmp-cksum-oobr-3	icmp-cksum-oobr-3.pcap		icmp-cksum-oobr-3.out	-vvv -e
icmp-cksum-oobr-4	icmp-cksum-oobr-4.pcap		icmp-cksum-oobr-4.out	-vvv -e
Out 00:16:3e:27:78:a2 ethertype IPv4 (0x0800), length 204: truncated-ip - 13723 bytes missing! (tos 0x72,ECT(0), ttl 64, id 9472, offset 0, flags [none], proto ICMP (1), length 13911, bad cksum 67ea (->8c0c)!)
    62.220.31.247 > 62.225.245.115: ICMP 62.220.31.247 udp port 1027 unreachable, length 13891
	(tos 0xa0, ttl 114, id 30054, offset 0, flags [none], proto UDP (17), length 13728, bad cksum 3f1f (->a1f)!)
    62.225.245.115.9109 > 62.220.31.247.1027: [bad udp cksum 0xdfe7 -> 0xdb95!] UDP, length 132
	MPLS extension v0 packet not supported
IP (0x0021), length 244: truncated-ip - 32768 bytes missing! (tos 0x0, ttl 254, id 59168, offset 0, flags [DF], proto ICMP (1), length 33008, bad cksum 7ade (->fabd)!)
    10.4.0.34 > 12.4.4.4: ICMP time exceeded in-transit, length 32988
	(tos 0x0, ttl 1, id 42321, offset 0, flags [none], proto UDP (17), length 40)
    12.4.4.4.42315 > 12.1.1.1.33440: [bad udp cksum 0x1000 -> 0xbad0!] UDP, length 12
	MPLS extension v2
	  Extended Payload Object (2), Class-Type: 14, length 80
	    0x0000:  0000 000f 0001 0000 0a0a 0a0a 3f54 6869
	    0x0010:  732d 6973 2d74 6865 2d6e 616d 652d 6f66
	    0x0020:  2d74 6865 2d49 6e74 6572 6661 6365 2d74
	    0x0030:  6861 742d 7765 2d61 7265 2d6c 6f6f 6b69
	    0x0040:  6e67 2d66 6f72 2d5b 3a2d 295d[|icmp]
00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 337: truncated-ip - 4096 bytes missing! (tos 0x0, ttl 64, id 30662, offset 0, flags [DF], proto ICMP (1), length 4419, bad cksum cdf9 (->bdf9)!)
    97.242.24.11 > 97.242.24.11: ICMP 97.242.24.11 udp port 162 unreachable, length 4399
	(tos 0x0, ttl 128, id 30661, offset 0, flags [DF], proto UDP (17), length 295)
    97.242.24.11.60377 > 97.242.24.11.162: [udp sum ok]  { SNMPv1 C="trap" { Trap(251)  .1.3.6.1.4.1.3830.1.1.2.2.1 97.242.24.11 enterpriseSpecific s=52 61498489 .1.3.6.1.4.1.3830.1.1.2.1.1.1=3 .1.3.6.1.4.1.3830.1.1.2.1.1.2=2 .1.3.6.1.4.1.3830.1.1.2.1.1.3="%SMSA-E-POLLERR, Polling the SMSC was not successful." .1.3.6.1.4.1.3830.1.1.2.1.1.4="OPCOM" .1.3.6.1.4.1.3830.1.1.2.1.1.5="28-OCT-2010 20:42:14.67" .1.3.6.1.4.1.3830.1.1.2.1.1.6="SMRL51" } } 
	MPLS extension v0 packet not supported
IP (0x0021), length 172: truncated-ip - 8192 bytes missing! (tos 0xc0, ttl 251, id 5047, offset 0, flags [none], proto ICMP (1), length 8360, bad cksum 7edb (->5edb)!)
    10.0.12.2 > 10.0.12.1: ICMP time exceeded in-transit, length 8340
	(tos 0x0, ttl 1, id 2574, offset 0, flags [none], proto UDP (17), length 28)
    10.0.12.1.49215 > 10.255.255.4.33435: [udp sum ok] UDP, length 0
	MPLS extension v2
	  MPLS Stack Entry Object (1), Class-Type: 1, length 8
	    label 16, exp 0, [S], ttl 1[|icmp]