>Synopsis:      display bug and complex length bug in tcpdump/print-icmp.c
>Category:      system
>Environment:
        System      : OpenBSD 7.2
        Details     : OpenBSD 7.2 (GENERIC.MP) #2: Thu Nov 24 23:53:03 MST 2022
                         
r...@syspatch-72-arm64.openbsd.org:/usr/src/sys/arch/arm64/compile/GENERIC.MP

        Architecture: OpenBSD.arm64
        Machine     : arm64
>Description:
        In the print-icmp.c code there is a while() near line 316 that goes
through Router Advertisements (icmp type 9 code 0) but doesn't advance struct
idp.  My patch adds this, yet I was unable to test it.

Also I'm not satisfied with the -vv option and printing the IP packet and
it's payload the way it is done.  Basically it passes print_ip() length
parameter with what's given on the wire (as payload of the icmp) which will
run through so many length checks in print_ip() it's not funny.  In fact
you could use this similar to a GRE specially crafted packet to increase
some length that may point beyond snaplen.  I'm thinking of some high length
like 0xffff being put in there.  I have done a calculation on what length it
should be instead only it looks ugly but is more correct.  Let me show you
the captured tcpdumps next section.

>How-To-Repeat:

Normal before:

root@echo# tcpdump -vv -n -i bse0 -s 1500 ip and icmp
tcpdump: listening on bse0, link-type EN10MB
17:51:32.250143 192.168.177.14 > 192.168.177.13: icmp: 192.168.177.14 udp port 
999 unreachable [icmp cksum ok] for 192.168.177.13.11143 > 192.168.177.14.999: 
udp 6 (ttl 64, id 6174, len 34) (ttl 255, id 42263, len 56)

With my patch:

root@echo# obj/tcpdump -vv -n -i bse0 -s 1500 ip and icmp
tcpdump: listening on bse0, link-type EN10MB
17:51:00.810291 192.168.177.14 > 192.168.177.13: icmp: 192.168.177.14 udp port 
999 unreachable [icmp cksum ok] for truncated-ip - 6 bytes 
missing!192.168.177.13.36685 > 192.168.177.14.999:  truncated-udp - 6 bytes 
missing![bad udp cksum 5445! -> 3689] udp 0 (ttl 64, id 52444, len 34) (ttl 
255, id 58823, len 56)
^C

As seen in the hexdump the packet is really missing data:

Normal before:

17:55:22.936602 192.168.177.14 > 192.168.177.13: icmp: 192.168.177.14 udp port 
999 unreachable [icmp cksum ok] for 192.168.177.13.37982 > 192.168.177.14.999: 
udp 6 (ttl 64, id 17991, len 34) (ttl 255, id 44077, len 56)
  0000: 4500 0038 ac2d 0000 ff01 2c2a c0a8 b10e  E..8.-....,*....
  0010: c0a8 b10d 0303 2466 0000 0000 4500 0022  ......$f....E.."
  0020: 4647 0000 4011 5117 c0a8 b10d c0a8 b10e  FG..@.Q.........
  0030: 945e 03e7 000e 4043                      .^....@C


With my patch:

17:56:09.560294 192.168.177.14 > 192.168.177.13: icmp: 192.168.177.14 udp port 
999 unreachable [icmp cksum ok] for truncated-ip - 6 bytes 
missing!192.168.177.13.22420 > 192.168.177.14.999:  truncated-udp - 6 bytes 
missing![bad udp cksum 0d7d! -> efc0] udp 0 (ttl 64, id 316, len 34) (ttl 255, 
id 20015, len 56)
  0000: 4500 0038 4e2f 0000 ff01 8a28 c0a8 b10e  E..8N/.....(....
  0010: c0a8 b10d 0303 2466 0000 0000 4500 0022  ......$f....E.."
  0020: 013c 0000 4011 9622 c0a8 b10d c0a8 b10e  .<..@.."........
  0030: 5794 03e7 000e 7d0d                      W.....}.

>Fix:
The patch:

Index: print-icmp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-icmp.c,v
retrieving revision 1.27
diff -u -p -u -r1.27 print-icmp.c
--- print-icmp.c        1 Dec 2021 18:28:46 -0000       1.27
+++ print-icmp.c        1 Mar 2023 16:49:55 -0000
@@ -319,6 +319,7 @@ icmp_print(const u_char *bp, u_int lengt
                            ipaddr_string(&idp->ird_addr),
                            EXTRACT_32BITS(&idp->ird_pref));
                        strlcat(buf, buf2, sizeof(buf));
+                       idp++;
                }
                }
                break;
@@ -385,9 +386,13 @@ icmp_print(const u_char *bp, u_int lengt
        }
        if (vflag > 1 && !ICMP_INFOTYPE(dp->icmp_type) &&
            TTEST(dp->icmp_ip)) {
+               int ilen;
                printf(" for ");
                oip = &dp->icmp_ip;
-               ip_print((u_char *)oip, ntohs(oip->ip_len));
+               ilen = length - ((u_char *)oip - (u_char *)bp);
+               if (ilen <= 0 || ilen > length)
+                       goto trunc;
+               ip_print((u_char *)oip, ilen);
        }
        return;
 trunc:


dmesg:
My host hasn't changed.

Reply via email to