>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
[email protected]:/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>