git: 7d810f27c16b - main - pf: Prevent pf dropping TCP state with crafted reset packet.

From: Kristof Provost <kp_at_FreeBSD.org>
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 */