>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>

Reply via email to