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