CVE-2017-13006 (Other, published 2017-09-14T06:29Z)
Description: value: The L2TP parser in tcpdump before 4.9.2 has a buffer over-read in print-l2tp.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-l2tp.c
tests/TESTLIST
tests/l2tp-avp-overflow.out
Commit message: CVE-2017-13006/L2TP: Check whether an AVP's content exceeds the AVP length.

It's not good enough to check whether all the data specified by the AVP
length was captured - you also have to check whether that length is
large enough for all the required data in the AVP.

This fixes a buffer over-read discovered by Yannick Formaggio.

Add a test using the capture file supplied by the reporter(s).


Vulnerable code:
l2tp_msgtype_print(netdissect_options *ndo, const u_char *dat)
	ND_PRINT((ndo, "%u", EXTRACT_16BITS(ptr))); ptr++;	/* Result Code */
	if (length > 2) {				/* Error Code (opt) */
	        ND_PRINT((ndo, "/%u", EXTRACT_16BITS(ptr))); ptr++;
	if (length > 4) {				/* Error Message (opt) */
		ND_PRINT((ndo, " "));
		print_string(ndo, (const u_char *)ptr, length - 4);
l2tp_proto_ver_print(netdissect_options *ndo, const uint16_t *dat)
l2tp_framing_cap_print(netdissect_options *ndo, const u_char *dat)
l2tp_bearer_cap_print(netdissect_options *ndo, const u_char *dat)
	if (length > 3) {
		print_string(ndo, dat+3, length-3);
l2tp_bearer_type_print(netdissect_options *ndo, const u_char *dat)
l2tp_framing_type_print(netdissect_options *ndo, const u_char *dat)
l2tp_proxy_auth_type_print(netdissect_options *ndo, const u_char *dat)
l2tp_proxy_auth_id_print(netdissect_options *ndo, const u_char *dat)
l2tp_call_errors_print(netdissect_options *ndo, const u_char *dat)
	val_h = EXTRACT_16BITS(ptr); ptr++;
	val_l = EXTRACT_16BITS(ptr); ptr++;
	val_h = EXTRACT_16BITS(ptr); ptr++;
	val_l = EXTRACT_16BITS(ptr); ptr++;
	val_h = EXTRACT_16BITS(ptr); ptr++;
	val_l = EXTRACT_16BITS(ptr); ptr++;
	val_h = EXTRACT_16BITS(ptr); ptr++;
	val_l = EXTRACT_16BITS(ptr); ptr++;
	val_h = EXTRACT_16BITS(ptr); ptr++;
	val_l = EXTRACT_16BITS(ptr); ptr++;
l2tp_accm_print(netdissect_options *ndo, const u_char *dat)
	val_h = EXTRACT_16BITS(ptr); ptr++;
	val_l = EXTRACT_16BITS(ptr); ptr++;
	ND_PRINT((ndo, "%04x, ", EXTRACT_16BITS(ptr))); ptr++;	/* Disconnect Code */
	ND_PRINT((ndo, "%04x ",  EXTRACT_16BITS(ptr))); ptr++;	/* Control Protocol Number */
			     "Direction-#%u", *((const u_char *)ptr++))));
	if (length > 5) {
		print_string(ndo, (const u_char *)ptr, length-5);
	/* After this point, no need to worry about truncation */
				l2tp_msgtype_print(ndo, (const u_char *)ptr);
				l2tp_proto_ver_print(ndo, ptr);
				l2tp_framing_cap_print(ndo, (const u_char *)ptr);
				l2tp_bearer_cap_print(ndo, (const u_char *)ptr);
				l2tp_bearer_type_print(ndo, (const u_char *)ptr);
				l2tp_framing_type_print(ndo, (const u_char *)ptr);
				l2tp_proxy_auth_type_print(ndo, (const u_char *)ptr);
				l2tp_proxy_auth_id_print(ndo, (const u_char *)ptr);
				l2tp_call_errors_print(ndo, (const u_char *)ptr);
				l2tp_accm_print(ndo, (const u_char *)ptr);

Fixed code:
l2tp_msgtype_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	/* Result Code */
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	ND_PRINT((ndo, "%u", EXTRACT_16BITS(ptr)));
	ptr++;
	length -= 2;

	/* Error Code (opt) */
	if (length == 0)
		return;
	if (length < 2) {
		ND_PRINT((ndo, " AVP too short"));
		return;
	ND_PRINT((ndo, "/%u", EXTRACT_16BITS(ptr)));
	ptr++;
	length -= 2;

	/* Error Message (opt) */
	if (length == 0)
		return;
	ND_PRINT((ndo, " "));
	print_string(ndo, (const u_char *)ptr, length);
l2tp_proto_ver_print(netdissect_options *ndo, const uint16_t *dat, u_int length)
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_framing_cap_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_bearer_cap_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	if (length < 3) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	dat += 3;
	length -= 3;
	if (length != 0) {
		print_string(ndo, dat, length);
l2tp_bearer_type_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_framing_type_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_proxy_auth_type_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_proxy_auth_id_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_call_errors_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	val_h = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	val_l = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	val_h = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	val_l = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	val_h = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	val_l = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	val_h = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	val_l = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	val_h = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	val_l = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
l2tp_accm_print(netdissect_options *ndo, const u_char *dat, u_int length)
	if (length < 2) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	val_h = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	val_l = EXTRACT_16BITS(ptr); ptr++; length -= 2;
	if (length < 4) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	if (length < 5) {
		ND_PRINT((ndo, "AVP too short"));
		return;
	}
	/* Disconnect Code */
	ND_PRINT((ndo, "%04x, ", EXTRACT_16BITS(dat)));
	dat += 2;
	length -= 2;
	/* Control Protocol Number */
	ND_PRINT((ndo, "%04x ",  EXTRACT_16BITS(dat)));
	dat += 2;
	length -= 2;
	/* Direction */
			     "Direction-#%u", EXTRACT_8BITS(ptr))));
	ptr++;
	length--;
	if (length != 0) {
		print_string(ndo, (const u_char *)ptr, length);

	/*
	 * After this point, we don't need to check whether we go past
	 * the length of the captured data; however, we *do* need to
	 * check whether we go past the end of the AVP.
	 */
				l2tp_msgtype_print(ndo, (const u_char *)ptr, len-6);
				l2tp_proto_ver_print(ndo, ptr, len-6);
				l2tp_framing_cap_print(ndo, (const u_char *)ptr, len-6);
				l2tp_bearer_cap_print(ndo, (const u_char *)ptr, len-6);
				if (len-6 < 8) {
					ND_PRINT((ndo, "AVP too short"));
					break;
				}
				if (len-6 < 2) {
					ND_PRINT((ndo, "AVP too short"));
					break;
				}
				if (len-6 < 16) {
					ND_PRINT((ndo, "AVP too short"));
					break;
				}
				if (len-6 < 4) {
					ND_PRINT((ndo, "AVP too short"));
					break;
				}
				l2tp_bearer_type_print(ndo, (const u_char *)ptr, len-6);
				l2tp_framing_type_print(ndo, (const u_char *)ptr, len-6);
				l2tp_proxy_auth_type_print(ndo, (const u_char *)ptr, len-6);
				l2tp_proxy_auth_id_print(ndo, (const u_char *)ptr, len-6);
				l2tp_call_errors_print(ndo, (const u_char *)ptr, len-6);
				l2tp_accm_print(ndo, (const u_char *)ptr, len-6);
# bad packets from Yannick Formaggio
l2tp-avp-overflow	l2tp-avp-overflow.pcap		l2tp-avp-overflow.out	-v

IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->ab7b)!)
    127.0.0.229.12416 > 127.0.128.1.1701:  l2tp:[TL](560/2056) AVP-#60963() |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) AVP-#48() |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 8752, bad cksum 1f51 (->3890)!)
    127.0.0.229.32767 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a90)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->ab5d)!)
    127.0.0.0.0 > 0.0.0.0.2048: UDP, bad length 17704 > 12308
[|ether]
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) AVP-#48() |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 8752, bad cksum 1f51 (->3890)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a90)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->ab5d)!)
    127.0.0.0.0 > 0.0.0.0.2048: UDP, bad length 17704 > 12308
[|ether]
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) AVP-#48() |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 8752, bad cksum 1f51 (->3890)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a90)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) ACCM(AVP too short) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) AVP-#48() |...
IP (tos 0x30, ttl 48, id 12331, offset 0, flags [none], proto UDP (17), length 8752, bad cksum 1f51 (->3890)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) VENDOR0001:ATTR0023(0530) |...
IP (tos 0x30, ttl 48, id 12336, offset 0, flags [none], proto UDP (17), length 12336, bad cksum 1f51 (->2a8b)!)
    127.0.0.229.12416 > 127.236.0.1.1701:  l2tp:[TL](560/2056) VENDOR0080:ATTR06a5(19e8) |...
EXIT CODE 00000100