CVE-2017-13003 (C, published 2017-09-14T06:29Z)
Description: value: The LMP parser in tcpdump before 4.9.2 has a buffer over-read in print-lmp.c:lmp_print().
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:
extract.h
print-lmp.c
tests/TESTLIST
tests/lmpv1_busyloop.out
Commit message: CVE-2017-13003/Clean up the LMP dissector.

Do a lot more bounds and length checks.

Add a EXTRACT_8BITS() macro, for completeness, and so as not to confuse
people into thinking that, to fetch a 1-byte value from a packet, they
need to use EXTRACT_16BITS() to fetch a 2-byte value and then use
shifting and masking to extract the desired byte.  Use that rather than
using EXTRACT_16BITS() to fetch a 2-byte value and then shifting and
masking to extract the desired byte.

Don't treat IPv4 addresses and unnumbered interface IDs the same; the
first should be printed as an IPv4 address but the latter should just be
printed as numbers.  Handle IPv6 addresses in more object types while
we're at it.

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

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


Vulnerable code:
#define EXTRACT_LE_8BITS(p) (*(p))
 * Support for LMP service discovery extensions (defined by UNI 1.0) added
 * by Manu Pathak (mapathak@cisco.com), May 2005
    int tlen,lmp_obj_len,lmp_obj_ctype,obj_tlen;
    int offset,subobj_type,subobj_len,total_subobj_len;
    int link_type;
        if(lmp_obj_len % 4 || lmp_obj_len < 4)
            return;

		bittok2str(lmp_obj_te_link_flag_values,
			EXTRACT_16BITS(obj_tptr)>>8)));
	    switch(lmp_obj_ctype) {
	    case LMP_CTYPE_IPV4:
		ND_PRINT((ndo, "\n\t    Flags: [%s]",
		bittok2str(lmp_obj_data_link_flag_values,
			"none",
			EXTRACT_16BITS(obj_tptr)>>8)));

	    case LMP_CTYPE_UNMD:
		total_subobj_len = lmp_obj_len - 16;
		offset = 12;
		while (total_subobj_len > 0 && hexdump == FALSE ) {
			subobj_type = EXTRACT_16BITS(obj_tptr+offset)>>8;
			subobj_len  = EXTRACT_16BITS(obj_tptr+offset)&0x00FF;
			ND_PRINT((ndo, "\n\t    Subobject, Type: %s (%u), Length: %u",
				tok2str(lmp_data_link_subobj,
					"Unknown",
					subobj_type),
					subobj_type,
					subobj_len));
			switch(subobj_type) {
			case INT_SWITCHING_TYPE_SUBOBJ:
				ND_PRINT((ndo, "\n\t      Switching Type: %s (%u)",
					tok2str(gmpls_switch_cap_values,
						"Unknown",
						EXTRACT_16BITS(obj_tptr+offset+2)>>8),
					EXTRACT_16BITS(obj_tptr+offset+2)>>8));
				ND_PRINT((ndo, "\n\t      Encoding Type: %s (%u)",
					tok2str(gmpls_encoding_values,
						"Unknown",
						EXTRACT_16BITS(obj_tptr+offset+2)&0x00FF),
					EXTRACT_16BITS(obj_tptr+offset+2)&0x00FF));
				bw.i = EXTRACT_32BITS(obj_tptr+offset+4);
				ND_PRINT((ndo, "\n\t      Min Reservable Bandwidth: %.3f Mbps",
                                       bw.f*8/1000000));
				bw.i = EXTRACT_32BITS(obj_tptr+offset+8);
				ND_PRINT((ndo, "\n\t      Max Reservable Bandwidth: %.3f Mbps",
                                       bw.f*8/1000000));
				break;
			case WAVELENGTH_SUBOBJ:
				ND_PRINT((ndo, "\n\t      Wavelength: %u",
					EXTRACT_32BITS(obj_tptr+offset+4)));
				break;
			default:
				/* Any Unknown Subobject ==> Exit loop */
				hexdump=TRUE;
				break;
			}
			total_subobj_len-=subobj_len;
			offset+=subobj_len;
		}

	    case LMP_CTYPE_UNMD:
		while (offset < (lmp_obj_len-(int)sizeof(struct lmp_object_header)) ) {
			ND_PRINT((ndo, "\n\t\t    Active: %s (%u)", 		(EXTRACT_32BITS(obj_tptr+offset+4)>>31) ?
			ND_PRINT((ndo, "\n\t\t    Direction: %s (%u)", (EXTRACT_32BITS(obj_tptr+offset+4)>>30)&0x1 ?
	    case LMP_CTYPE_UNMD:
		while (offset < (lmp_obj_len-(int)sizeof(struct lmp_object_header)) ) {

				  EXTRACT_16BITS(obj_tptr)>>8)));
		       EXTRACT_16BITS(obj_tptr) & 0x00FF));
		link_type = EXTRACT_16BITS(obj_tptr)>>8;
		if (link_type == LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SDH) {
				   EXTRACT_16BITS(obj_tptr) & 0x00FF),
			   EXTRACT_16BITS(obj_tptr) & 0x00FF));
		}
		if (link_type == LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SONET) {
				   EXTRACT_16BITS(obj_tptr) & 0x00FF),
			   EXTRACT_16BITS(obj_tptr) & 0x00FF));
				  EXTRACT_16BITS(obj_tptr+2)>>8)));
				  EXTRACT_16BITS(obj_tptr+2)>>8 & 0x00FF)));
			   EXTRACT_16BITS(obj_tptr+6) & 0x00FF)));
			   EXTRACT_16BITS(obj_tptr+2) & 0x00FF)));

Fixed code:
/*
 * For 8-bit values; provided for the sake of completeness.  Byte order
 * isn't relevant, and alignment isn't an issue.
 */
#define EXTRACT_8BITS(p)	(*(p))
#define EXTRACT_LE_8BITS(p)	(*(p))

 * Support for LMP service discovery extensions (defined by OIF UNI 1.0)
 * added by Manu Pathak (mapathak@cisco.com), May 2005
/* OIF UNI 1.0: http://www.oiforum.com/public/documents/OIF-UNI-01.0.pdf */
static int
lmp_print_data_link_subobjs(netdissect_options *ndo, const u_char *obj_tptr,
                            int total_subobj_len, int offset)
{
    int hexdump = FALSE;
    int subobj_type, subobj_len;

    union { /* int to float conversion buffer */
        float f;
        uint32_t i;
    } bw;

    while (total_subobj_len > 0 && hexdump == FALSE ) {
	subobj_type = EXTRACT_8BITS(obj_tptr+offset);
	subobj_len  = EXTRACT_8BITS(obj_tptr+offset+1);
	ND_PRINT((ndo, "\n\t    Subobject, Type: %s (%u), Length: %u",
		tok2str(lmp_data_link_subobj,
			"Unknown",
			subobj_type),
			subobj_type,
			subobj_len));
	if (subobj_len < 4) {
	    ND_PRINT((ndo, " (too short)"));
	    break;
	}
	if ((subobj_len % 4) != 0) {
	    ND_PRINT((ndo, " (not a multiple of 4)"));
	    break;
	}
	if (total_subobj_len < subobj_len) {
	    ND_PRINT((ndo, " (goes past the end of the object)"));
	    break;
	}
	switch(subobj_type) {
	case INT_SWITCHING_TYPE_SUBOBJ:
	    ND_PRINT((ndo, "\n\t      Switching Type: %s (%u)",
		tok2str(gmpls_switch_cap_values,
			"Unknown",
			EXTRACT_8BITS(obj_tptr+offset+2)),
		EXTRACT_8BITS(obj_tptr+offset+2)));
	    ND_PRINT((ndo, "\n\t      Encoding Type: %s (%u)",
		tok2str(gmpls_encoding_values,
			"Unknown",
			EXTRACT_8BITS(obj_tptr+offset+3)),
		EXTRACT_8BITS(obj_tptr+offset+3)));
	    bw.i = EXTRACT_32BITS(obj_tptr+offset+4);
	    ND_PRINT((ndo, "\n\t      Min Reservable Bandwidth: %.3f Mbps",
                bw.f*8/1000000));
	    bw.i = EXTRACT_32BITS(obj_tptr+offset+8);
	    ND_PRINT((ndo, "\n\t      Max Reservable Bandwidth: %.3f Mbps",
                bw.f*8/1000000));
	    break;
	case WAVELENGTH_SUBOBJ:
	    ND_PRINT((ndo, "\n\t      Wavelength: %u",
		EXTRACT_32BITS(obj_tptr+offset+4)));
	    break;
	default:
	    /* Any Unknown Subobject ==> Exit loop */
	    hexdump=TRUE;
	    break;
	}
	total_subobj_len-=subobj_len;
	offset+=subobj_len;
    }
    return (hexdump);
}

    u_int tlen,lmp_obj_len,lmp_obj_ctype,obj_tlen;
    u_int offset;
    u_int link_type;
    if (tlen < sizeof(const struct lmp_common_header)) {
        ND_PRINT((ndo, " (too short)"));
        return;
    }
    if (tlen > len) {
        ND_PRINT((ndo, " (too long)"));
        tlen = len;
    }
        if (lmp_obj_len < 4) {
            ND_PRINT((ndo, " (too short)"));
            return;
        }
        if ((lmp_obj_len % 4) != 0) {
            ND_PRINT((ndo, " (not a multiple of 4)"));
            return;
        }

                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 16) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 8) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
	    switch(lmp_obj_ctype) {
	    case LMP_CTYPE_IPV4:
                if (obj_tlen != 12) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
		    bittok2str(lmp_obj_te_link_flag_values,
			EXTRACT_8BITS(obj_tptr))));
                if (obj_tlen != 36) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
		ND_PRINT((ndo, "\n\t    Flags: [%s]",
		    bittok2str(lmp_obj_te_link_flag_values,
			"none",
			EXTRACT_8BITS(obj_tptr))));

		ND_PRINT((ndo, "\n\t    Local Link-ID: %s (0x%08x)"
		       "\n\t    Remote Link-ID: %s (0x%08x)",
                       ip6addr_string(ndo, obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr+4),
                       ip6addr_string(ndo, obj_tptr+20),
                       EXTRACT_32BITS(obj_tptr+20)));
                break;

                if (obj_tlen != 12) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
		ND_PRINT((ndo, "\n\t    Flags: [%s]",
		    bittok2str(lmp_obj_te_link_flag_values,
			"none",
			EXTRACT_8BITS(obj_tptr))));

		ND_PRINT((ndo, "\n\t    Local Link-ID: %u (0x%08x)"
		       "\n\t    Remote Link-ID: %u (0x%08x)",
                       EXTRACT_32BITS(obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr+8),
                       EXTRACT_32BITS(obj_tptr+8)));
		break;

                if (obj_tlen < 12) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
	        ND_PRINT((ndo, "\n\t    Flags: [%s]",
		    bittok2str(lmp_obj_data_link_flag_values,
			"none",
			EXTRACT_8BITS(obj_tptr))));
		if (lmp_print_data_link_subobjs(ndo, obj_tptr, obj_tlen - 12, 12))
		    hexdump=TRUE;

                if (obj_tlen < 36) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
	        ND_PRINT((ndo, "\n\t    Flags: [%s]",
		    bittok2str(lmp_obj_data_link_flag_values,
			"none",
			EXTRACT_8BITS(obj_tptr))));
                ND_PRINT((ndo, "\n\t    Local Interface ID: %s (0x%08x)"
                       "\n\t    Remote Interface ID: %s (0x%08x)",
                       ip6addr_string(ndo, obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr+4),
                       ip6addr_string(ndo, obj_tptr+20),
                       EXTRACT_32BITS(obj_tptr+20)));

		if (lmp_print_data_link_subobjs(ndo, obj_tptr, obj_tlen - 36, 36))
		    hexdump=TRUE;
		break;

	    case LMP_CTYPE_UNMD:
                if (obj_tlen < 12) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
	        ND_PRINT((ndo, "\n\t    Flags: [%s]",
		    bittok2str(lmp_obj_data_link_flag_values,
			"none",
			EXTRACT_8BITS(obj_tptr))));
                ND_PRINT((ndo, "\n\t    Local Interface ID: %u (0x%08x)"
                       "\n\t    Remote Interface ID: %u (0x%08x)",
                       EXTRACT_32BITS(obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr+8),
                       EXTRACT_32BITS(obj_tptr+8)));

		if (lmp_print_data_link_subobjs(ndo, obj_tptr, obj_tlen - 12, 12))
		    hexdump=TRUE;
		break;

                if (obj_tlen != 20) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
		while (offset+8 <= obj_tlen) {
			ND_PRINT((ndo, "\n\t\t    Active: %s (%u)",
				(EXTRACT_32BITS(obj_tptr+offset+4)>>31) ?
			ND_PRINT((ndo, "\n\t\t    Direction: %s (%u)",
				(EXTRACT_32BITS(obj_tptr+offset+4)>>30)&0x1 ?

		offset = 0;
		/* Decode pairs: <Interface_ID (16 bytes), Channel_status (4 bytes)> */
		while (offset+20 <= obj_tlen) {
			ND_PRINT((ndo, "\n\t    Interface ID: %s (0x%08x)",
			ip6addr_string(ndo, obj_tptr+offset),
			EXTRACT_32BITS(obj_tptr+offset)));

			ND_PRINT((ndo, "\n\t\t    Active: %s (%u)",
				(EXTRACT_32BITS(obj_tptr+offset+16)>>31) ?
						"Allocated" : "Non-allocated",
				(EXTRACT_32BITS(obj_tptr+offset+16)>>31)));

			ND_PRINT((ndo, "\n\t\t    Direction: %s (%u)",
				(EXTRACT_32BITS(obj_tptr+offset+16)>>30)&0x1 ?
						"Transmit" : "Receive",
				(EXTRACT_32BITS(obj_tptr+offset+16)>>30)&0x1));

			ND_PRINT((ndo, "\n\t\t    Channel Status: %s (%u)",
					tok2str(lmp_obj_channel_status_values,
					"Unknown",
					EXTRACT_32BITS(obj_tptr+offset+16)&0x3FFFFFF),
			EXTRACT_32BITS(obj_tptr+offset+16)&0x3FFFFFF));
			offset+=20;
		}
                break;

	    case LMP_CTYPE_UNMD:
		offset = 0;
		/* Decode pairs: <Interface_ID (4 bytes), Channel_status (4 bytes)> */
		while (offset+8 <= obj_tlen) {
			ND_PRINT((ndo, "\n\t    Interface ID: %u (0x%08x)",
			EXTRACT_32BITS(obj_tptr+offset),
			EXTRACT_32BITS(obj_tptr+offset)));

			ND_PRINT((ndo, "\n\t\t    Active: %s (%u)",
				(EXTRACT_32BITS(obj_tptr+offset+4)>>31) ?
						"Allocated" : "Non-allocated",
				(EXTRACT_32BITS(obj_tptr+offset+4)>>31)));

			ND_PRINT((ndo, "\n\t\t    Direction: %s (%u)",
				(EXTRACT_32BITS(obj_tptr+offset+4)>>30)&0x1 ?
						"Transmit" : "Receive",
				(EXTRACT_32BITS(obj_tptr+offset+4)>>30)&0x1));

			ND_PRINT((ndo, "\n\t\t    Channel Status: %s (%u)",
					tok2str(lmp_obj_channel_status_values,
					"Unknown",
					EXTRACT_32BITS(obj_tptr+offset+4)&0x3FFFFFF),
			EXTRACT_32BITS(obj_tptr+offset+4)&0x3FFFFFF));
			offset+=8;
		}
                break;

		while (offset+4 <= obj_tlen) {

		offset = 0;
		while (offset+16 <= obj_tlen) {
			ND_PRINT((ndo, "\n\t    Interface ID: %s (0x%08x)",
			ip6addr_string(ndo, obj_tptr+offset),
			EXTRACT_32BITS(obj_tptr+offset)));
			offset+=16;
		}
                break;

	    case LMP_CTYPE_UNMD:
		offset = 0;
		while (offset+4 <= obj_tlen) {
			ND_PRINT((ndo, "\n\t    Interface ID: %u (0x%08x)",
			EXTRACT_32BITS(obj_tptr+offset),
			EXTRACT_32BITS(obj_tptr+offset)));
			offset+=4;
		}
                break;

                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
				  EXTRACT_8BITS(obj_tptr))));
		       EXTRACT_8BITS(obj_tptr+1)));
                if (obj_tlen != 16) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
		link_type = EXTRACT_8BITS(obj_tptr);
		switch (link_type) {
		case LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SDH:
				   EXTRACT_8BITS(obj_tptr+1)),
			   EXTRACT_8BITS(obj_tptr+1)));
		    break;
		case LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SONET:
				   EXTRACT_8BITS(obj_tptr+1)),
			   EXTRACT_8BITS(obj_tptr+1)));
		    break;
				  EXTRACT_8BITS(obj_tptr+2))));
				  EXTRACT_8BITS(obj_tptr+3))));
                if (obj_tlen != 8) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
			   EXTRACT_8BITS(obj_tptr+7))));
                if (obj_tlen != 4) {
                    ND_PRINT((ndo, " (not correct for object)"));
                    break;
                }
			   EXTRACT_8BITS(obj_tptr+3))));
lmpv1_busyloop		lmpv1_busyloop.pcap		lmpv1_busyloop.out	-vvv -e
00:80:ad:91:d8:6f > 00:0b:64:00:10:72, ethertype IPv4 (0x0800), length 725: (tos 0x0, ttl 128, id 35978, offset 0, flags [none], proto UDP (17), length 711)
    168.152.32.1.701 > 168.152.32.39.701: [udp sum ok] 
	LMPv1, msg-type: Config, Flags: [Control Channel Down], length: 257
	  Data Link Object (12), Class-Type: IPv4 (1) Flags: [non-negotiable], length: 516
	    Flags: [Allocated for user traffic]
	    Local Interface ID: 0.0.2.0 (0x00000200)
	    Remote Interface ID: 2.0.2.0 (0x02000200)
	    Subobject, Type: Wavelength (2), Length: 0 (too short)
	    0x0000:  0253 e10b 0000 0200 0200 0200 0200 0200
	    0x0010:  0200 0200 0200 0200 0200 0200 0200 0200
	    0x0020:  0200 0200 0200 0200 0200 0200 0200 0200
	    0x0030:  0200 0200 0200 0200 0200 0200 0200 0200
	    0x0040:  0200 0280 6d00 0200 0200 0200 0200 0200
	    0x0050:  0200 0200 0200 0200 0200 0200 0200 0200
	    0x0060:  0200 0200 0200 0200 0200 0200 0200 0200
	    0x0070:  0200 0200 0200 0200 0200 0200 0200 8202
	    0x0080:  0002 0002 0002 0002 0002 0002 0002 0002
	    0x0090:  0002 0002 0002 0002 0002 0002 0002 0002
	    0x00a0:  0002 0002 0002 0002 0002 0002 0002 0002
	    0x00b0:  2002 0002 0002 0002 0002 0002 0002 0002
	    0x00c0:  0000 0200 0200 0002 0002 0000 0200 0200
	    0x00d0:  0002 0002 0000 0200 0200 0002 0002 0000
	    0x00e0:  0200 0200 0002 0002 0000 0200 0200 0002
	    0x00f0:  0002 0000 0200 0200 0002 0002 0000 0200
	    0x0100:  0200 0002 0002 0000 0200 0200 0002 0002
	    0x0110:  0000 0200 0200 0002 0002 0000 0200 0200
	    0x0120:  0002 0002 0000 0200 0200 0002 0002 0000
	    0x0130:  0200 0200 0002 0002 0000 0200 0200 0002
	    0x0140:  0002 0000 0200 0200 0002 0002 0000 0200
	    0x0150:  0200 0002 0002 0000 0200 0200 0002 0002
	    0x0160:  0000 0200 0200 0002 0002 0000 0200 0200
	    0x0170:  0002 0002 0000 0200 0200 0002 0002 0000
	    0x0180:  0200 0200 0002 0002 0000 0200 0200 0002
	    0x0190:  0002 0000 0200 0200 0002 0002 0000 0200
	    0x01a0:  0200 0002 0002 0000 0200 0200 0002 0002
	    0x01b0:  0000 0200 0200 0002 0002 0000 0200 0200
	    0x01c0:  0002 0002 0000 0200 0200 0002 0002 0000
	    0x01d0:  0200 0200 0002 0002 0000 0200 0200 0002
	    0x01e0:  0002 0000 0200 0200 0002 0002 0000 0200
	    0x01f0:  0200 0002 0002 0000 0200 0200 0002 0002
	  Unknown Object (0), Class-Type: Unknown (0) Flags: [non-negotiable], length: 512
		 packet exceeded snapshot