Skip to content

Commit 33040b7

Browse files
committed
ICMP: make sure we don't run past the end of a TLV for RFC 5837 objects.
As reported to tcpdump-security. While we're at it, fix the check for the interface name length - it includes the one-octet length of the length, as well as the length of the name, so it must be at least 1. Add the capture file from the report.
1 parent b80f964 commit 33040b7

File tree

4 files changed

+85
-35
lines changed

4 files changed

+85
-35
lines changed

print-icmp.c

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ Interface IP Address Sub-Object
360360
struct icmp_interface_information_ipaddr_subobject_t {
361361
nd_uint16_t afi;
362362
nd_uint16_t reserved;
363-
nd_byte ip_addr[1];
363+
nd_byte ip_addr[];
364364
};
365365

366366
/*
@@ -388,7 +388,7 @@ struct icmp_interface_identification_ipaddr_subobject_t {
388388
nd_uint16_t afi;
389389
nd_uint8_t addrlen;
390390
nd_uint8_t reserved;
391-
nd_byte ip_addr[1];
391+
nd_byte ip_addr[];
392392
};
393393

394394
/* prototypes */
@@ -410,30 +410,45 @@ icmp_tstamp_print(u_int tstamp)
410410
return buf;
411411
}
412412

413+
#define CHECK_TLEN(len) \
414+
ND_TCHECK_LEN(obj_tptr, len); \
415+
if (obj_tlen < (len)) { \
416+
return obj_len; \
417+
}
418+
419+
#define UPDATE_TLEN(len) \
420+
obj_tlen -= (len)
421+
422+
#define UPDATE_TPTR_AND_TLEN(len) \
423+
obj_tptr += (len); \
424+
obj_tlen -= (len)
425+
413426
static int
414-
print_icmp_multipart_ext_object(netdissect_options *ndo, const uint8_t *obj_tptr)
427+
print_icmp_multipart_ext_object(netdissect_options *ndo, const uint8_t *obj_ptr)
415428
{
416-
u_int obj_tlen, obj_class_num, obj_ctype;
429+
u_int obj_len, obj_class_num, obj_ctype;
417430
const struct icmp_multipart_ext_object_header_t *icmp_multipart_ext_object_header;
431+
const uint8_t *obj_tptr;
432+
u_int obj_tlen;
418433

419-
icmp_multipart_ext_object_header = (const struct icmp_multipart_ext_object_header_t *)obj_tptr;
420-
obj_tlen = GET_BE_U_2(icmp_multipart_ext_object_header->length);
434+
icmp_multipart_ext_object_header = (const struct icmp_multipart_ext_object_header_t *)obj_ptr;
435+
obj_len = GET_BE_U_2(icmp_multipart_ext_object_header->length);
421436
obj_class_num = GET_U_1(icmp_multipart_ext_object_header->class_num);
422437
obj_ctype = GET_U_1(icmp_multipart_ext_object_header->ctype);
423-
obj_tptr += sizeof(struct icmp_multipart_ext_object_header_t);
424438

425439
ND_PRINT("\n\t %s (%u), Class-Type: %u, length %u",
426440
tok2str(icmp_multipart_ext_obj_values,"unknown",obj_class_num),
427441
obj_class_num,
428442
obj_ctype,
429-
obj_tlen);
443+
obj_len);
430444

431445
/* infinite loop protection */
432-
if ((obj_class_num == 0) ||
433-
(obj_tlen < sizeof(struct icmp_multipart_ext_object_header_t))) {
446+
if ((obj_class_num == 0) || /* XXX - why is this necessary? */
447+
(obj_len < sizeof(struct icmp_multipart_ext_object_header_t))) {
434448
return -1;
435449
}
436-
obj_tlen -= sizeof(struct icmp_multipart_ext_object_header_t);
450+
obj_tptr = obj_ptr + sizeof(struct icmp_multipart_ext_object_header_t);
451+
obj_tlen = obj_len - sizeof(struct icmp_multipart_ext_object_header_t);
437452

438453
switch (obj_class_num) {
439454
case MPLS_STACK_ENTRY_OBJECT_CLASS:
@@ -442,6 +457,7 @@ print_icmp_multipart_ext_object(netdissect_options *ndo, const uint8_t *obj_tptr
442457
{
443458
uint32_t raw_label;
444459

460+
CHECK_TLEN(4);
445461
raw_label = GET_BE_U_4(obj_tptr);
446462
ND_PRINT("\n\t label %u, tc %u", MPLS_LABEL(raw_label), MPLS_TC(raw_label));
447463
if (MPLS_STACK(raw_label))
@@ -464,7 +480,6 @@ print_icmp_multipart_ext_object(netdissect_options *ndo, const uint8_t *obj_tptr
464480
| Interface Role| Rsvd1 | Rsvd2 |ifIndex| IPAddr| name | MTU |
465481
+-------+-------+-------+-------+-------+-------+-------+-------+
466482
*/
467-
const uint8_t *offset;
468483
u_int interface_role, if_index_flag, ipaddr_flag, name_flag, mtu_flag;
469484

470485
interface_role = (obj_ctype & 0xc0) >> 6;
@@ -477,92 +492,119 @@ print_icmp_multipart_ext_object(netdissect_options *ndo, const uint8_t *obj_tptr
477492
tok2str(icmp_interface_information_role_values,
478493
"an unknown interface role",interface_role));
479494

480-
offset = obj_tptr;
481-
482495
if (if_index_flag) {
483-
ND_PRINT("\n\t Interface Index: %u", GET_BE_U_4(offset));
484-
offset += 4;
496+
CHECK_TLEN(4);
497+
ND_PRINT("\n\t Interface Index: %u", GET_BE_U_4(obj_tptr));
498+
UPDATE_TPTR_AND_TLEN(4);
485499
}
486500
if (ipaddr_flag) {
501+
uint16_t afi;
487502
const struct icmp_interface_information_ipaddr_subobject_t *ipaddr_subobj;
488503

489504
ND_PRINT("\n\t IP Address sub-object: ");
490-
ipaddr_subobj = (const struct icmp_interface_information_ipaddr_subobject_t *) offset;
491-
switch (GET_BE_U_2(ipaddr_subobj->afi)) {
505+
ipaddr_subobj = (const struct icmp_interface_information_ipaddr_subobject_t *) obj_tptr;
506+
507+
CHECK_TLEN(sizeof *ipaddr_subobj);
508+
509+
/* AFI */
510+
afi = GET_BE_U_2(ipaddr_subobj->afi);
511+
512+
/* Reserved space */
513+
ND_TCHECK_1(ipaddr_subobj->reserved);
514+
515+
UPDATE_TPTR_AND_TLEN(sizeof *ipaddr_subobj);
516+
517+
switch (afi) {
492518
case 1:
519+
CHECK_TLEN(4);
493520
ND_PRINT("%s", GET_IPADDR_STRING(ipaddr_subobj->ip_addr));
494-
offset += 4;
521+
UPDATE_TPTR_AND_TLEN(4);
495522
break;
496523
case 2:
524+
CHECK_TLEN(16);
497525
ND_PRINT("%s", GET_IP6ADDR_STRING(ipaddr_subobj->ip_addr));
498-
offset += 16;
526+
UPDATE_TPTR_AND_TLEN(16);
499527
break;
500528
default:
501529
ND_PRINT("Unknown Address Family Identifier");
502530
return -1;
503531
}
504-
offset += 4;
505532
}
506533
if (name_flag) {
507534
uint8_t inft_name_length_field;
508535
const struct icmp_interface_information_ifname_subobject_t *ifname_subobj;
509536

510-
ifname_subobj = (const struct icmp_interface_information_ifname_subobject_t *) offset;
537+
ifname_subobj = (const struct icmp_interface_information_ifname_subobject_t *) obj_tptr;
538+
CHECK_TLEN(1);
511539
inft_name_length_field = GET_U_1(ifname_subobj->length);
540+
512541
ND_PRINT("\n\t Interface Name");
513-
if (inft_name_length_field == 0) {
542+
if (inft_name_length_field < 1) {
514543
ND_PRINT(" [length %u]", inft_name_length_field);
515544
nd_print_invalid(ndo);
516545
break;
517546
}
547+
CHECK_TLEN(inft_name_length_field);
518548
if (inft_name_length_field % 4 != 0) {
519549
ND_PRINT(" [length %u != N x 4]", inft_name_length_field);
520550
nd_print_invalid(ndo);
521-
offset += inft_name_length_field;
551+
UPDATE_TPTR_AND_TLEN(inft_name_length_field);
522552
break;
523553
}
524554
if (inft_name_length_field > 64) {
525555
ND_PRINT(" [length %u > 64]", inft_name_length_field);
526556
nd_print_invalid(ndo);
527-
offset += inft_name_length_field;
557+
UPDATE_TPTR_AND_TLEN(inft_name_length_field);
528558
break;
529559
}
530560
ND_PRINT(", length %u: ", inft_name_length_field);
531561
nd_printjnp(ndo, ifname_subobj->if_name,
532562
inft_name_length_field - 1);
533-
offset += inft_name_length_field;
563+
UPDATE_TPTR_AND_TLEN(inft_name_length_field);
534564
}
535565
if (mtu_flag) {
536-
ND_PRINT("\n\t MTU: %u", GET_BE_U_4(offset));
537-
offset += 4;
566+
CHECK_TLEN(4);
567+
ND_PRINT("\n\t MTU: %u", GET_BE_U_4(obj_tptr));
568+
UPDATE_TPTR_AND_TLEN(4);
538569
}
539570
break;
540571
}
541572

542573
case INTERFACE_IDENTIFICATION_OBJECT_CLASS:
543574
switch (obj_ctype) {
544575
case 1:
545-
ND_PRINT("\n\t Interface Name, length %d: ", obj_tlen);
576+
ND_PRINT("\n\t Interface Name, length %u: ", obj_tlen);
546577
nd_printjnp(ndo, obj_tptr, obj_tlen);
547578
break;
548579
case 2:
580+
CHECK_TLEN(4);
549581
ND_PRINT("\n\t Interface Index: %u", GET_BE_U_4(obj_tptr));
550582
break;
551583
case 3:
552584
{
553585
const struct icmp_interface_identification_ipaddr_subobject_t *id_ipaddr_subobj;
586+
uint16_t afi;
587+
uint8_t addrlen;
588+
554589
ND_PRINT("\n\t IP Address sub-object: ");
555590
id_ipaddr_subobj = (const struct icmp_interface_identification_ipaddr_subobject_t *) obj_tptr;
556-
switch (GET_BE_U_2(id_ipaddr_subobj->afi)) {
591+
CHECK_TLEN(sizeof *id_ipaddr_subobj);
592+
afi = GET_BE_U_2(id_ipaddr_subobj->afi);
593+
addrlen = GET_U_1(id_ipaddr_subobj->addrlen);
594+
ND_TCHECK_1(id_ipaddr_subobj->reserved);
595+
UPDATE_TLEN(sizeof *id_ipaddr_subobj);
596+
597+
CHECK_TLEN(addrlen);
598+
switch (afi) {
557599
case 1:
558-
if (GET_U_1(id_ipaddr_subobj->addrlen) != 4) {
559-
ND_PRINT("[length %d != 4] ", GET_U_1(id_ipaddr_subobj->addrlen));
600+
if (addrlen != 4) {
601+
ND_PRINT("[length %d != 4] ", addrlen);
560602
}
561603
ND_PRINT("%s", GET_IPADDR_STRING(id_ipaddr_subobj->ip_addr));
562604
break;
563605
case 2:
564-
if (GET_U_1(id_ipaddr_subobj->addrlen) != 16) {
565-
ND_PRINT("[length %d != 16] ", GET_U_1(id_ipaddr_subobj->addrlen));
606+
if (addrlen != 16) {
607+
ND_PRINT("[length %d != 16] ", addrlen);
566608
}
567609
ND_PRINT("%s", GET_IP6ADDR_STRING(id_ipaddr_subobj->ip_addr));
568610
break;
@@ -582,7 +624,7 @@ print_icmp_multipart_ext_object(netdissect_options *ndo, const uint8_t *obj_tptr
582624
print_unknown_data(ndo, obj_tptr, "\n\t ", obj_tlen);
583625
break;
584626
}
585-
return obj_tlen + sizeof(struct icmp_multipart_ext_object_header_t);
627+
return obj_len;
586628
}
587629

588630
void

tests/TESTLIST

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ icmp-rfc8335-missing-bytes icmp-rfc8335-missing-bytes.pcap icmp-rfc8335-missing-
232232
icmp6-rfc8335 icmp6-rfc8335.pcap icmp6-rfc8335.out
233233
icmp6-rfc8335-v icmp6-rfc8335.pcap icmp6-rfc8335-v.out -v
234234

235+
# ICMPv4 -- pcap from tcpdump-security
236+
icmp_ext_oob_poc icmp_ext_oob_poc.pcap icmp_ext_oob_poc.out -v
237+
235238
# ICMPv6
236239
icmpv6 icmpv6.pcap icmpv6.out -vv
237240
icmpv6_opt24-v icmpv6_opt24.pcap icmpv6_opt24-v.out -v

tests/icmp_ext_oob_poc.out

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
1 2025-05-14 11:44:45.360234 IP (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto ICMP (1), length 44)
2+
192.168.1.100 > 192.168.1.200: ICMP extended echo request, id 0, seq 0, length 24
3+
Remote Interface, checksum 0xcccb (correct), length 10
4+
Interface Information Object (2), Class-Type: 12, length 6
5+
Interface Role: Incoming IP Interface

tests/icmp_ext_oob_poc.pcap

98 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)