>Synopsis: TCP stack accepts packets with invalid ack number >Category: kernel >Environment: System : OpenBSD 7.6 Details : OpenBSD 7.6 (GENERIC.MP) #338: Mon Sep 30 08:55:35 MDT 2024 dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Architecture: OpenBSD.amd64 Machine : amd64 >Description: When a TCP connection is in the ESTABLISHED state, OpenBSD's TCP implementation incorrectly accepts a packet with an acknowledgment number (ACK) smaller than expected and containing the FIN+ACK flags. In my test, I sent such a FIN+ACK packet with a smaller ACK value and OpenBSD accepted it, responded with FIN+ACK. According to RFC 9293, if the ACK is a duplicate, it can be ignored. If the ACK acks something not yet sent, then send an ACK, drop the segment, and return. I captured the trace using tcpdump on OpenBSD machine, and attached it at the end of this report. >How-To-Repeat: The following python scripts can trigger this issue. ``` #!/usr/bin/env python3 import sys, random, time from scapy.all import IP, TCP, sr1, send, conf def main(): if len(sys.argv) != 3: print(f"Usage: {sys.argv[0]} <target_ip> <target_port>") sys.exit(1) target_ip = sys.argv[1] target_port = int(sys.argv[2]) source_port = random.randint(40000, 50000) init_seq = random.randint(1_000_000_000, 2_000_000_000) conf.verb = 0 syn = IP(dst=target_ip)/TCP(sport=source_port, dport=target_port, flags="S", seq=init_seq, ack=0x56555c7a, window=0xfae0, urgptr=4096, options=[ ('MSS', 1460), ('NOP', None), ('EOL', None) ]) syn_ack = sr1(syn, timeout=2) if not syn_ack or syn_ack.flags & 0x12 == 0: print("[-] SYN-ACK not received") sys.exit(1) ack_seq = init_seq + 1 ack_ack = syn_ack.seq + 1 ack = IP(dst=target_ip)/TCP(sport=source_port, dport=target_port, flags="A", seq=ack_seq, ack=ack_ack, window=502) send(ack) fin_seq = ack_seq fin_ack = IP(dst=target_ip)/TCP(sport=source_port, dport=target_port, flags="FA", seq=fin_seq, ack=ack_ack-5, window=502) send(fin_ack) time.sleep(0.3) end_seq = ack_seq + 1 end_ack = ack_ack + 1 end = IP(dst=target_ip)/TCP(sport=source_port, dport=target_port, flags="A", seq=end_seq, ack=end_ack, window=502, options=[ ('NOP', None), ('NOP', None), ('EOL', None), ('EOL', None) ]) send(end) if __name__ == "__main__": main() ``` tcpdump: 19:12:37.554374 192.168.31.251.41256 > 192.168.31.230.12347: S 1218760741:1218760741(0) win 64224 <mss 1460,nop,eol> 19:12:37.554666 192.168.31.230.12347 > 192.168.31.251.41256: S 1368512783:1368512783(0) ack 1218760742 win 16384 <mss 1460> (DF) 19:12:37.582491 192.168.31.251.41256 > 192.168.31.230.12347: . ack 1 win 502 19:12:37.601062 192.168.31.251.41256 > 192.168.31.230.12347: F 1:1(0) ack 4294967292 win 502 19:12:37.601241 192.168.31.230.12347 > 192.168.31.251.41256: . ack 2 win 17520 (DF) 19:12:37.601952 192.168.31.230.12347 > 192.168.31.251.41256: F 1:1(0) ack 2 win 17520 (DF) 19:12:37.926110 192.168.31.251.41256 > 192.168.31.230.12347: . ack 2 win 502 <nop,nop,eol>