tcp: strict RFC 5961 RST sequence validation (contributes to #1132)#13165
tcp: strict RFC 5961 RST sequence validation (contributes to #1132)#13165ibondarenko1 wants to merge 1 commit into
Conversation
nybidari
left a comment
There was a problem hiding this comment.
Minor nits! Otherwise LGTM
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| package handle_reset_rfc5961_test |
There was a problem hiding this comment.
Instead of creating a new test file, we can add these tests in either pkg/tcpip/transport/tcp/test/e2e/e2e.go or pkg/tcpip/transport/tcp/test/e2e/tcp_test.go
| // number falls outside the advertised receive window is silently dropped. | ||
| // Pre-RFC-5961 behavior (RFC 793 page 37) already silently dropped these, | ||
| // so this asserts the unchanged path. | ||
| func TestRFC5961_RSTOutOfWindowIsDropped(t *testing.T) { |
There was a problem hiding this comment.
Remove the RFC5961 from the test names, the comment about the behavior change and RFC can stay.
| // The strict-match rule defends against off-path blind RST injection. | ||
| // Linux has implemented it since version 3.6 (2012); see | ||
| // net/ipv4/tcp_input.c tcp_validate_incoming(). Contribution to | ||
| // gvisor.dev/issues/1132. |
There was a problem hiding this comment.
Remove this line from the comments: "Contribution to gvisor.dev/issues/1132"
handleReset in pkg/tcpip/transport/tcp/connect.go accepted any RST whose sequence number was within the advertised receive window (rcv.go:70-81 acceptable). The source comment cited RFC 793 page 37. With a 4 MB advertised window the per-packet probability of a blind off-path RST hitting an in-window sequence is roughly window/2^32 (about 0.1 percent). An off-path attacker that knows the 4-tuple could terminate an established TCP connection in a small number of packets. CVE-2024-10603 (commit cbdb2c6, 2023-11-10) added source-port randomization, which raised the practical cost by a factor of about 2^16. With both source-port randomization AND the existing window- based RST check, the joint per-packet blind probability is around window/2^48. The remaining gap is the RST acceptance rule itself. Rewrite handleReset to implement RFC 5961 section 3.2: * Segment sequence number out of window: silent drop. (Unchanged.) * Segment sequence number in window but not exactly equal to RCV.NXT: send a challenge ACK via the existing rate-limited helper e.snd.maybeSendOutOfWindowAck(s) and drop the segment. (New. Previously such a RST aborted the connection.) * Segment sequence number exactly equals RCV.NXT: accept and reset the connection. (Unchanged for legitimate peers.) The challenge ACK path reuses the same helper used by the existing RFC 5961 section 4.1 implementation at connect.go:1290-1312 (SYN-in-window). The rate-limit is provided by maybeSendOutOfWindowAck which already defends against the CVE-2016-5696 class of challenge-ACK side channels on the Linux side. Linux net/ipv4/tcp_input.c tcp_validate_incoming has implemented RFC 5961 section 3.2 since version 3.6 (2012). gVisor partially implements RFC 5961: section 4.1 is present, this commit adds section 3.2. Tests added to pkg/tcpip/transport/tcp/test/e2e/tcp_test.go: * TestRSTOutOfWindowIsDropped (silent drop, unchanged path) * TestRSTInWindowNotExactSendsChallengeAck (new behavior, challenge ACK observed via sniffer) * TestRSTExactMatchAbortsConnection (legitimate RST still aborts) Tested: bazel build //pkg/tcpip/transport/tcp:tcp bazel test //pkg/tcpip/transport/tcp/test/e2e:tcp_test bazel test //pkg/tcpip/transport/tcp/test/e2e:rcv_test Hardening, not an advisory: class is denial of service (off-path blind RST termination); Google VRP does not bounty DoS, and gVisor's published threat model does not claim RFC 5961 compliance. References: RFC 5961 section 3.2 (RST acceptance) RFC 5961 section 4.1 (SYN-in-window challenge ACK, connect.go:1290) Linux net/ipv4/tcp_input.c tcp_validate_incoming CVE-2016-5696 (Off-Path TCP Exploits, Cao et al., USENIX Sec 2016) CVE-2024-10603 (TCP source-port randomization, gVisor cbdb2c6)
3bc57ba to
484ef12
Compare
|
@nybidari thanks for the review. All three nits addressed in commit
Net diff vs upstream: +177/-37 across two files ( |
Contribution to issue #1132 (open since 2019-08, "Validate segment handling in Netstack as per RFC 793 page 69"). That issue documents the gap explicitly: "We also need to verify which parts have been updated by RFC5961 to make TCP robust against attacks." This PR adds RFC 5961 section 3.2 strict-match RST acceptance.
Problem
pkg/tcpip/transport/tcp/connect.gohandleResetaccepts any RST whose sequence number is within the advertised receive window (pkg/tcpip/transport/tcp/rcv.go:70-81acceptable). The source comment cites RFC 793 page 37.With a 4 MB advertised window the per-packet probability of a blind off-path RST hitting an in-window sequence is roughly
window / 2^32(~0.1 percent). An off-path attacker that knows the 4-tuple can terminate an established TCP connection in a small number of packets.CVE-2024-10603 (commit
cbdb2c61b1"Randomize TCP source port selection", 2023-11-10) already added source-port randomization, which raised the practical cost by a factor of ~2^16. With both source-port randomization AND the existing window-based RST check, the joint per-packet blind probability is ~window / 2^48. The remaining gap is the RST acceptance rule itself.RFC 5961 section 3.2
RFC 5961 section 3.2 tightens RFC 793 page 37:
Linux
net/ipv4/tcp_input.ctcp_validate_incominghas implemented this since version 3.6 (2012). gVisor partially implements RFC 5961: section 4.1 (SYN-in-window challenge ACK) is present atconnect.go:1290-1312, with an inline RFC 5961 link in the comment. Section 3.2 (RST strict-match) was the remaining gap that #1132 tracks.Change
handleResetis rewritten to RFC 5961 section 3.2:RCV.NXT: send a challenge ACK via the existing rate-limited helpere.snd.maybeSendOutOfWindowAck(s), then drop. (NEW.)RCV.NXT: accept and reset the connection. (Unchanged for legitimate peers.)The challenge ACK path reuses the same helper used by the existing RFC 5961 section 4.1 implementation in this same file. The rate-limit inside
maybeSendOutOfWindowAckalso defends against the CVE-2016-5696 class of challenge-ACK side channels.Tests
New file
pkg/tcpip/transport/tcp/test/e2e/handle_reset_rfc5961_test.goadds three focused regression tests:TestRFC5961_RSTOutOfWindowIsDropped(silent drop, unchanged path)TestRFC5961_RSTInWindowNotExactSendsChallengeAck(NEW behavior: no abort, challenge ACK observed via the test sniffer)TestRFC5961_RSTExactMatchAbortsConnection(legitimate RST still aborts)Local run:
Challenge-ACK behavior verified via the test sniffer:
RST at SEQ=1814 (in window, not equal to RCV.NXT=790) is answered with a challenge ACK at SND.NXT=854776498 / ACK=790, instead of a connection reset.
//pkg/tcpip/transport/tcp/test/e2e:rcv_testalso passes on this branch (independent verification that the receiver path is unchanged for non-RST segments).bazel build //pkg/tcpip/transport/tcp:tcpbuilds clean againstmaster.Framing
Hardening contribution. Class is denial of service (off-path blind RST termination of an established connection). Google VRP does not bounty DoS, and gVisor's published threat model does not claim RFC 5961 compliance. This PR is a Linux-parity uplift contributing to the existing open issue, not a security advisory.
References
net/ipv4/tcp_input.ctcp_validate_incoming(RFC 5961 section 3 since Linux 3.6, 2012)cbdb2c61b1, 2023-11)