The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=7d810f27c16b1bb151ed701b040febef8f8909fe
commit 7d810f27c16b1bb151ed701b040febef8f8909fe Author: Kristof Provost <[email protected]> AuthorDate: 2026-09-03 13:57:58 +0000 Commit: Kristof Provost <[email protected]> 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 <[email protected]>, 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 */
