git: 7d810f27c16b - main - pf: Prevent pf dropping TCP state with crafted reset packet.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 07 Sep 2026 08:38:09 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=7d810f27c16b1bb151ed701b040febef8f8909fe
commit 7d810f27c16b1bb151ed701b040febef8f8909fe
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2026-09-03 13:57:58 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2026-09-07 08:37:46 +0000
pf: Prevent pf dropping TCP state with crafted reset packet.
Revision 1.1212 of pf.c weakened the TCP reset check in stateful
connection tracking to let legitimate resets pass in the backwards
window. Such a reset is accepted only if its acknowledgment number
matches perfectly. But as a workaround for broken stacks, pf
replaces an acknowledgment number of 0 in a reset with the tracked
sequence of the peer. Then the perfect match always succeeds, and
an attacker can spoof resets more easily than intended. Use the
acknowledgment number from the wire, before the workaround has
modified it.
discovered by Minghao Zhang; OK sashan@
Obtained from: OpenBSD, bluhm <bluhm@openbsd.org>, 1e0a1f4b82
Sponsored by: Rubicon Communications, LLC ("Netgate")
---
sys/netpfil/pf/pf.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 322b10c3fce6..9c9509900ea9 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -7290,7 +7290,7 @@ pf_tcp_track_full(struct pf_kstate *state, struct pf_pdesc *pd,
{
struct tcphdr *th = &pd->hdr.tcp;
u_int16_t win = ntohs(th->th_win);
- u_int32_t ack, end, data_end, seq, orig_seq;
+ u_int32_t ack, orig_ack, end, data_end, seq, orig_seq;
u_int8_t sws, dws;
int ackskew;
@@ -7389,6 +7389,7 @@ pf_tcp_track_full(struct pf_kstate *state, struct pf_pdesc *pd,
if (tcp_get_flags(th) & TH_FIN)
end++;
}
+ orig_ack = ack;
if ((tcp_get_flags(th) & TH_ACK) == 0) {
/* Let it pass through the ack skew check */
@@ -7442,7 +7443,7 @@ pf_tcp_track_full(struct pf_kstate *state, struct pf_pdesc *pd,
(orig_seq == src->seqlo + 1) || (orig_seq + 1 == src->seqlo) ||
/* Require an exact/+1 sequence match on resets when possible */
(SEQ_GEQ(orig_seq, src->seqlo - (dst->max_win << dws)) &&
- SEQ_LEQ(orig_seq, src->seqlo + 1) && ackskew == 0 &&
+ SEQ_LEQ(orig_seq, src->seqlo + 1) && orig_ack == dst->seqlo &&
(th->th_flags & (TH_ACK|TH_RST)) == (TH_ACK|TH_RST)))) {
/* Allow resets to match sequence window if ack is perfect match */