On Mon, Oct 26 2020, "Peter J. Philipp" <[email protected]> wrote:
> On Sun, Oct 25, 2020 at 12:15:22AM +0200, Jeremie Courreges-Anglas wrote:
>> 
>> dunno why the strange combination of To/Cc headers so I'll keep bugs@ in Cc:
>
> My answer is inline below:
>
>> On Sat, Oct 24 2020, [email protected] wrote:
>> >>Synopsis:  a specially crafted packet can set tcpdump into an endless loop
>> >>Category:  system
>> >>Environment:
>> >    System      : OpenBSD 6.8
>> >    Details     : OpenBSD 6.8 (GENERIC.MP) #98: Sun Oct  4 18:13:26 MDT 2020
>> >                     
>> > [email protected]:/usr/src/sys/arch/amd64/compile/GENERIC.MP
>> >
>> >    Architecture: OpenBSD.amd64
>> >    Machine     : amd64
>> >>Description:
>> >    I was (un)fortunate enough to have this treat of a bug from a cracker
>> > on the 'net.  Thanks!  They sent me an infinite loop in tcpdump.  I bug 
>> > hunted
>> > and shared my findings on the misc@ mailing list.  I wasn't sure becuase 
>> > I'm
>> > not the best code reader so I crafted a DOS to exploit this infinite loop.
>> > I have mailed OpenBSD off-lists with this proof of concept code.
>> >>How-To-Repeat:
>> > before patch:
>> >
>> > tcpdump -v -n -i lo0 port 8888
>> >
>> > 10:30:54.667969 127.0.0.1.2123 > 127.0.0.1.8888: [udp sum ok]  GTPv1-C 
>> > (teid 0, len 0) [MBMS Support] [MBMS Support] [MBMS Support] [MBMS 
>> > Support] [MBMS Support] [MBMS Support] [MBMS Support] [MBMS Support] [MBMS 
>> > Support] [MBMS Support] [MBMS Support] [MBMS Support] ...
>> > 2 packets received by filter
>> > 0 packets dropped by kernel
>> >
>> > This was triggered with netcat:
>> >
>> > nc -up 2123 localhost 8888 < dos.packet
>> >
>> >>Fix:
>> > after patch:
>> >
>> > tcpdump.p: listening on lo0, link-type LOOP
>> > 10:43:41.005389 127.0.0.1.2123 > 127.0.0.1.8888: [udp sum ok]  GTPv1-C 
>> > (teid 0, len 0) [|GTPv1-C] (ttl 64, id 58060, len 41)
>> > ^C
>> > 2 packets received by filter
>> > 0 packets dropped by kernel
>> > spica# tcpdump.p -v -n -i lo0 -Xp-sp1500pport88888
>> > tcpdump.p: listening on lo0, link-type LOOP
>> > 10:44:11.956464 127.0.0.1.2123 > 127.0.0.1.8888: [udp sum ok]  GTPv1-C 
>> > (teid 0, len 0) [|GTPv1-C] (ttl 64, id 18693, len 41)
>> >   0000: 4500 0029 4905 0000 4011 33bd 7f00 0001     E..)[email protected].....
>> >   0010: 7f00 0001 084b 22b8 0015 a2bd 3400 0000     .....K".....4...
>> >   0020: 0000 0000 0000 0001 00                      .........
>> >
>> > ^C
>> > 2 packets received by filter
>> > 0 packets dropped by kernel
>> >
>> > The patch looks like follows:
>> >
>> > Index: print-gtp.c
>> > ===================================================================
>> > RCS file: /cvs/src/usr.sbin/tcpdump/print-gtp.c,v
>> > retrieving revision 1.12
>> > diff -u -p -u -r1.12 print-gtp.c
>> > --- print-gtp.c    20 May 2020 01:20:37 -0000      1.12
>> > +++ print-gtp.c    24 Oct 2020 08:56:00 -0000
>> > @@ -927,6 +927,8 @@ gtp_v1_print(const u_char *cp, u_int len
>> >  
>> >                    /* Header length is a 4 octet multiplier. */
>> 
>> I've never seen GTP in the wild but indeed a length of zero is invalid.
>> In case it can help other reviewers:
>
> I was possibly not exploited as a natural DNS packet can trigger this.  And
> that's how I found this bug in the first place by tcpdumping DNS.  Consider
> this program to create the crafted packet:
>
> beta$ ./bad-packet > evil.2            
> beta$ hexdump -C evil.2                
> 00000000  34 00 00 00 00 01 00 00  00 00 00 02 00 00 00 00  |4...............|
> 00000010
> beta$ vi bad-packet.c                  
> beta$ cc -g -o bad-packet bad-packet.c 
> beta$ ./bad-packet > evil.2            
> beta$ nc -up 2123 127.0.0.1 8888 < evil.2
>
> On the other screen then where I had a tcpdump on port 8888 (pretend it was 
> 53):
>
> rting] [MS Info Change Reporting] [MS Info Change Reporting] [MS Info Change 
> Reporting] [MS Info Change Reporting] [MS Info Change Reporting] [MS Info 
> Change Reporting] [MS Inf^C
> 19 packets received by filter
>
> Same scenario only a bit different.  The DNS packet then consists of:
>
> (from RFC 1035)
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>     |                      ID                       |
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>     |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>     |                    QDCOUNT                    |
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>     |                    ANCOUNT                    |
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>     |                    NSCOUNT                    |
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>     |                    ARCOUNT                    |
>     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
>
> an ID=0x3400 (netbyte order)
> QDCOUNT of 1  (comes with any answer)
> ARCOUNT of 2  (say an OPT and a TSIG payload)
>
> is an answer packet.

> Port must be 2123 for the query.

The source port must be 2123 for tcpdump to try to interpret this as
a GTP packet.  This doesn't look helpful if you're just looking at DNS
packets, but that's the way UDP packets are currently treated.

>From print-udp.c:

--8<--
                else if (ISPORT(GTP_C_PORT) || ISPORT(GTP_U_PORT) ||
                    ISPORT(GTP_PRIME_PORT))
                        gtp_print(cp, length, sport, dport);
-->8--

> The program to create evil.2 is:
>
> #include <sys/types.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> #include <unistd.h>
>
> int
> main(void)
> {
>         char packet[16];
>
>         memset(&packet, 0, sizeof(packet));
>
>         packet[0] = 0x34;
>
>         packet[5] = 1;
>         packet[9] = 1;
>         packet[11] = 2;
>
>         write(STDOUT_FILENO, packet, sizeof(packet));
> }

Thanks for the reproducer, works here.

>>   
>> https://www.etsi.org/deliver/etsi_ts/129000_129099/129060/15.05.00_60/ts_129060v150500p.pdf
>> 
>> >                    hlen = (int)p[0] * 4;
>> > +                  if (hlen == 0)
>> > +                          goto trunc;
>> 
>> LGTM, though I'd suggest printing why we're bailing out.
>
> What would be a good error message?

I suggested another diff in my reply (quoted below).

Looking for oks, with or without the header type value printed.

The result:

shannon ~$ sudo tcpdump -nvvvi lo0 udp
tcpdump: listening on lo0, link-type LOOP
22:21:13.670145 127.0.0.1.2123 > 127.0.0.1.50000: [udp sum ok]  GTPv1-C (teid 
65536, len 0) [Invalid zero-length header 2] [|GTPv1-C] (ttl 64, id 44435, len 
44)

> Best Regards,
>
> -peter
>
>> 
>> Index: print-gtp.c
>> ===================================================================
>> RCS file: /d/cvs/src/usr.sbin/tcpdump/print-gtp.c,v
>> retrieving revision 1.12
>> diff -u -p -p -u -r1.12 print-gtp.c
>> --- print-gtp.c      20 May 2020 01:20:37 -0000      1.12
>> +++ print-gtp.c      24 Oct 2020 22:02:47 -0000
>> @@ -927,6 +927,11 @@ gtp_v1_print(const u_char *cp, u_int len
>>  
>>                      /* Header length is a 4 octet multiplier. */
>>                      hlen = (int)p[0] * 4;
>> +                    if (hlen == 0) {
>> +                            printf(" [Invalid zero-length header %u]",
>> +                                nexthdr);
>> +                            goto trunc;
>> +                    }
>>                      TCHECK2(p[0], hlen);
>>  
>>                      switch (nexthdr) {
>> 
>> -- 
>> jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE
>

-- 
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply via email to