CVE-2017-13030 (Other, published 2017-09-14T06:29Z)
Description: value: The PIM parser in tcpdump before 4.9.2 has a buffer over-read in print-pim.c, several functions.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/the-tcpdump-group/tcpdump
Files changed:
print-pim.c
tests/TESTLIST
tests/heapoverflow-in_checksum.out
tests/pim_header_asan.out
Commit message: CVE-2017-13030/PIM: Redo bounds checks and add length checks.

Use ND_TCHECK macros to do bounds checking, and add length checks before
the bounds checks.

Add a bounds check that the review process found was missing.

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.

Update one test output file to reflect the changes.


Vulnerable code:
	ND_TCHECK2(bp[6], 2);
	unsigned_relts_print(ndo, EXTRACT_16BITS(&bp[6]));
	bp += 8;
	len -= 8;
		ND_TCHECK2(bp[4], sizeof(struct in_addr));
		if (EXTRACT_32BITS(&bp[4]) != 0xffffffff)
			ND_PRINT((ndo, "/%s", ipaddr_string(ndo, &bp[4])));
		ND_TCHECK2(bp[8], 4);
		njoin = EXTRACT_16BITS(&bp[8]);
		nprune = EXTRACT_16BITS(&bp[10]);
		bp += 12;
		len -= 12;
			ipaddr_string(ndo, &bp[2]), bp[1] & 0x3f));
	register const u_char *ep;
	ep = (const u_char *)ndo->ndo_snapend;
	if (bp >= ep)
		return;

		if (ndo->ndo_vflag)
		ND_TCHECK(bp[4]);
		switch (bp[4] & 0x3) {
		if (bp[4] & 0xfc)
			ND_PRINT((ndo, " [rsvd=0x%02x]", bp[4] & 0xfc));
		ND_TCHECK(bp[5]);
		nentries = bp[5];
		bp += 6; len -= 6;
	register const u_char *ep;
	ep = (const u_char *)ndo->ndo_snapend;
	if (bp >= ep)
		return;
static int pimv2_addr_len;

                 const u_char *bp, enum pimv2_addrtype at, int silent)
	int len, hdrlen;
	ND_TCHECK(bp[0]);

	if (pimv2_addr_len == 0) {
			len = sizeof(struct in_addr);
			len = sizeof(struct in6_addr);
		switch (pimv2_addr_len) {
		len = pimv2_addr_len;
		ND_TCHECK2(bp[0], len);
		return hdrlen + len;
		ND_TCHECK2(bp[0], len + 2);
		return hdrlen + 2 + len;
		bp += 4;
		while (bp < ep) {
			ND_TCHECK2(bp[0], 4 + olen);
						advance = pimv2_addr_print(ndo, ptr, pimv2_unicast, 0);
						if (advance < 0) {
							ND_PRINT((ndo, "..."));
							break;
						}
		ND_TCHECK2(*(bp + 4), PIMV2_REGISTER_FLAG_LEN);
		          EXTRACT_32BITS(bp+4))));
		bp += 8; len -= 8;
		bp += 4; len -= 4;
		if (bp >= ep)
			break;
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_group, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		if (bp >= ep)
			break;
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		bp += 4; len -= 4;
			if (bp >= ep)
				break;
			if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
				ND_PRINT((ndo, "..."));
				break;
			}
		if (bp + 4 > ep)
			break;
			if (bp >= ep)
				goto jp_done;
			if ((advance = pimv2_addr_print(ndo, bp, pimv2_group, 0)) < 0) {
				ND_PRINT((ndo, "...)"));
				goto jp_done;
			}
			if (bp + 4 > ep) {
				ND_PRINT((ndo, "...)"));
				goto jp_done;
			}
				if ((advance = pimv2_addr_print(ndo, bp, pimv2_source, 0)) < 0) {
					ND_PRINT((ndo, "...)"));
					goto jp_done;
				}
				if ((advance = pimv2_addr_print(ndo, bp, pimv2_source, 0)) < 0) {
					ND_PRINT((ndo, "...)"));
					goto jp_done;
				}
	jp_done:
		bp += 4;
		if (bp + sizeof(uint16_t) >= ep) break;
		bp += sizeof(uint16_t);
		if (bp >= ep) break;
		if (bp + 1 >= ep) break;
		if (bp >= ep) break;
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
			if ((advance = pimv2_addr_print(ndo, bp, pimv2_group, 0))
			    < 0) {
				ND_PRINT((ndo, "...)"));
				goto bs_done;
			}
			if (bp >= ep) {
				ND_PRINT((ndo, "...)"));
				goto bs_done;
			}
			if (bp + 1 >= ep) {
				ND_PRINT((ndo, "...)"));
				goto bs_done;
			}
				if ((advance = pimv2_addr_print(ndo, bp,
								0)) < 0) {
					ND_PRINT((ndo, "...)"));
					goto bs_done;
				}
				if (bp + 1 >= ep) {
					ND_PRINT((ndo, "...)"));
					goto bs_done;
				}
				if (bp + 2 >= ep) {
					ND_PRINT((ndo, "...)"));
					goto bs_done;
				}
	   bs_done:
		bp += 4; len -= 4;
		if (bp >= ep)
			break;
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_group, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		if (bp >= ep)
			break;
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		if (bp + 8 > ep)
			break;
		bp += 4;
		if (bp >= ep) break;
		if (bp + 1 >= ep) break;
		if (bp + 3 >= ep) break;
		if (bp >= ep) break;
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
			if ((advance = pimv2_addr_print(ndo, bp, pimv2_group, 0))
			    < 0) {
				ND_PRINT((ndo, "..."));
				break;
			}
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_group, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		if ((advance = pimv2_addr_print(ndo, bp, pimv2_unicast, 0)) < 0) {
			ND_PRINT((ndo, "..."));
			break;
		}
		ND_TCHECK2(bp[0], 2);
	Hello, RFC2117-encoding, cksum 0x3030 (unverified)[|pim]

Fixed code:
	if (len < sizeof(struct in_addr))
		goto trunc;
	bp += 4;
	len -= 4;
	if (len < 4)
		goto trunc;
	ND_TCHECK2(bp[2], 2);
	unsigned_relts_print(ndo, EXTRACT_16BITS(&bp[2]));
	bp += 4;
	len -= 4;
	if (len < 4)
		goto trunc;
		if (len < 4)
			goto trunc;
		bp += 4;
		len -= 4;
		if (len < 4)
			goto trunc;
		ND_TCHECK2(bp[0], sizeof(struct in_addr));
		if (EXTRACT_32BITS(&bp[0]) != 0xffffffff)
			ND_PRINT((ndo, "/%s", ipaddr_string(ndo, &bp[0])));
		bp += 4;
		len -= 4;
		if (len < 4)
			goto trunc;
		ND_TCHECK2(bp[0], 4);
		njoin = EXTRACT_16BITS(&bp[0]);
		nprune = EXTRACT_16BITS(&bp[2]);
		bp += 4;
		len -= 4;
			if (len < 6)
				goto trunc;
			    ipaddr_string(ndo, &bp[2]),
			    bp[1] & 0x3f));
		if (ndo->ndo_vflag) {
			if (len < 8)
				goto trunc;
		}
	if (len < 8)
		goto trunc;
		if (len < 4)
			goto trunc;
		bp += 4;
		len -= 4;
		if (len < 1)
			goto trunc;
		ND_TCHECK(bp[0]);
		switch (bp[0] & 0x3) {
		if (bp[0] & 0xfc)
			ND_PRINT((ndo, " [rsvd=0x%02x]", bp[0] & 0xfc));
		bp += 1;
		len -= 1;
		if (len < 1)
			goto trunc;
		ND_TCHECK(bp[0]);
		nentries = bp[0];
		bp += 1;
		len -= 1;
			if (len < 6)
				goto trunc;
	ND_TCHECK(pim->pim_typever);

trunc:
	ND_PRINT((ndo, "[|pim]"));
	return;
                 const u_char *bp, u_int len, enum pimv2_addrtype at,
                 u_int addr_len, int silent)
	int hdrlen;
	if (addr_len == 0) {
		if (len < 2)
			goto trunc;
			addr_len = (u_int)sizeof(struct in_addr);
			addr_len = (u_int)sizeof(struct in6_addr);
		switch (addr_len) {
	len -= hdrlen;
		if (len < addr_len)
			goto trunc;
		ND_TCHECK2(bp[0], addr_len);
		return hdrlen + addr_len;
		if (len < addr_len + 2)
			goto trunc;
		ND_TCHECK2(bp[0], addr_len + 2);
		return hdrlen + 2 + addr_len;
	int pimv2_addr_len;
	if (len < 2)
		goto trunc;
	if (len < 4)
		goto trunc;
	ND_TCHECK(pim->pim_cksum);
	bp += 4;
	len -= 4;
		while (len > 0) {
			if (len < 4)
				goto trunc;
			len -= 4;
			if (len < olen)
				goto trunc;
			ND_TCHECK2(bp[0], olen);
					u_int plen = len;
						advance = pimv2_addr_print(ndo, ptr, plen, pimv2_unicast, pimv2_addr_len, 0);
						if (advance < 0)
							goto trunc;
						plen -= advance;
			len -= olen;
		if (len < 4)
			goto trunc;
		ND_TCHECK2(*bp, PIMV2_REGISTER_FLAG_LEN);
		          EXTRACT_32BITS(bp))));
		bp += 4; len -= 4;
		if (len == 0)
			goto trunc;
		ND_TCHECK(ip->ip_vhl);
			ND_TCHECK(ip->ip_dst);
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
			goto trunc;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
			goto trunc;
			if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
				goto trunc;
		if (len < 4)
			goto trunc;
		ND_TCHECK2(*bp, 4);
			if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
				goto trunc;
			if (len < 4)
				goto trunc;
			ND_TCHECK2(*bp, 4);
				if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_source, pimv2_addr_len, 0)) < 0)
					goto trunc;
				if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_source, pimv2_addr_len, 0)) < 0)
					goto trunc;
		if (len < 2)
			goto trunc;
		ND_TCHECK_16BITS(bp);
		bp += 2;
		len -= 2;
		if (len < 1)
			goto trunc;
		ND_TCHECK(bp[0]);
		if (len < 2)
			goto trunc;
		ND_TCHECK(bp[2]);
		len -= 2;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
			goto trunc;
		len -= advance;
			if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
				goto trunc;
			len -= advance;
			if (len < 1)
				goto trunc;
			ND_TCHECK(bp[0]);
			if (len < 2)
				goto trunc;
			ND_TCHECK(bp[1]);
			if (len < 4)
				goto trunc;
			len -= 4;
				if ((advance = pimv2_addr_print(ndo, bp, len,
								pimv2_addr_len,
								0)) < 0)
					goto trunc;
				len -= advance;
				if (len < 2)
					goto trunc;
				ND_TCHECK_16BITS(bp);
				if (len < 3)
					goto trunc;
				ND_TCHECK(bp[2]);
				if (len < 4)
					goto trunc;
				len -= 4;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
			goto trunc;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
			goto trunc;
		if (len < 8)
			goto trunc;
		ND_TCHECK2(*bp, 8);
		if (len < 1)
			goto trunc;
		ND_TCHECK(bp[0]);
		if (len < 2)
			goto trunc;
		ND_TCHECK(bp[1]);
		if (len < 4)
			goto trunc;
		ND_TCHECK_16BITS(&bp[2]);
		len -= 4;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
			goto trunc;
		len -= advance;
			if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
				goto trunc;
			len -= advance;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
			goto trunc;
		len -= advance;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
			goto trunc;
		len -= advance;
		if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
			goto trunc;
		len -= advance;
		if (len < 2)
			goto trunc;
		ND_TCHECK_16BITS(bp);
pim_header_asan		pim_header_asan.pcap		pim_header_asan.out		-v
	Hello, RFC2117-encoding, cksum 0x3030 (unverified)
	  Unknown Option (12336), length 12336, Value: [|pim]
IP6 (class 0x76, flowlabel 0xf6767, hlim 109, next-header PIM (103) payload length: 30311) 6767:6767:6767:8267:6767:6765:6767:6767 > 6700:80:74:24:2424:2424:2424:2509: PIMv2, length 30311
	Bootstrap, RFC2117-encoding[|pim]