patches for easier mirroring, to eliminate a special copy, to make www.freebsd.org/security a full copy of security.freebsd.org and be eventually be the same. For now files are just sitting there. The symlinks are missing. Discussed on: www (repository location) Discussed with: simon (so)
508 lines
19 KiB
Text
508 lines
19 KiB
Text
-----BEGIN PGP SIGNED MESSAGE-----
|
|
|
|
=============================================================================
|
|
FreeBSD-SA-98:07 Security Advisory
|
|
FreeBSD, Inc.
|
|
|
|
Topic: TCP RST denial of sevice
|
|
|
|
Category: core
|
|
Module: kernel
|
|
Announced: 1998-10-13
|
|
Affects: FreeBSD 2.2.* (before 2.2.8R), FreeBSD-stable and
|
|
FreeBSD-current before the correction date.
|
|
Corrected: FreeBSD-current as of 1998/09/11
|
|
FreeBSD-stable as of 1998/09/16
|
|
FreeBSD only: Yes
|
|
|
|
Patches: ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-98:07/
|
|
|
|
Vulnerable:
|
|
|
|
|
|
I. Background
|
|
|
|
TCP/IP connections are controlled through a series of packets that are
|
|
receieved by the two computers involved in the connection. Old, stale
|
|
connections are reset with a packet called a RST packet. The RST
|
|
packets have a sequence number in them that must be valid according to
|
|
certain rules in the standards.
|
|
|
|
|
|
II. Problem Description
|
|
|
|
A denail of service attack can be launched against FreeBSD systems
|
|
running without one of the patches supplied later in this message.
|
|
Using a flaw in the interpreation of sequence numbers in the RST
|
|
packet, malicious users can terminate connections of other users at
|
|
will.
|
|
|
|
|
|
III. Impact
|
|
|
|
Some TCP connections will be broken. This can range from a minor
|
|
inconvenience to a major problem depending on the nature of the
|
|
attackers and what they attack. This attack requires knowledge of the
|
|
TCP connection 4-tuple (source IP, source port, destination IP and
|
|
destination port). If even one of these items is unknown, then the
|
|
attack will not succeed. Users without priviledge of the destination
|
|
machine, however, can find the source IP and source port numbers with
|
|
the netstat command and can effect this attack. Also, intruders that
|
|
are able to capture raw network traffic on the network the target
|
|
machine resides will also have enough information to launch this
|
|
attack. It is also possible for an attacker to send a huge flood of
|
|
packets, hoping that they will get lucky just once (which is all they
|
|
need to attack a specific connection).
|
|
|
|
This vulnerability has been discussed in the security list called
|
|
BUGTRAQ and exploit programs are circulating to take advantage of this
|
|
flaw.
|
|
|
|
This attack has been reported most often as being used against people
|
|
connected to irc servers.
|
|
|
|
IV. Workaround
|
|
|
|
None.
|
|
|
|
V. Solution
|
|
|
|
Here is the patch that will apply to 2.2-stable systems from before
|
|
September 16, 1998. -stable systems after that date do not suffer
|
|
from this problem. It will also apply to FreeBSD 2.2.6 and 2.2.7.
|
|
|
|
|
|
Index: tcp_input.c
|
|
===================================================================
|
|
RCS file: /home/cvsup/freebsd/CVS/src/sys/netinet/tcp_input.c,v
|
|
retrieving revision 1.54.2.10
|
|
retrieving revision 1.54.2.11
|
|
diff -u -r1.54.2.10 -r1.54.2.11
|
|
--- tcp_input.c 1998/05/18 17:12:44 1.54.2.10
|
|
+++ tcp_input.c 1998/09/16 17:35:17 1.54.2.11
|
|
@@ -972,17 +972,99 @@
|
|
|
|
/*
|
|
* States other than LISTEN or SYN_SENT.
|
|
- * First check timestamp, if present.
|
|
+ * First check the RST flag and sequence number since reset segments
|
|
+ * are exempt from the timestamp and connection count tests. This
|
|
+ * fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix
|
|
+ * below which allowed reset segments in half the sequence space
|
|
+ * to fall though and be processed (which gives forged reset
|
|
+ * segments with a random sequence number a 50 percent chance of
|
|
+ * killing a connection).
|
|
+ * Then check timestamp, if present.
|
|
* Then check the connection count, if present.
|
|
* Then check that at least some bytes of segment are within
|
|
* receive window. If segment begins before rcv_nxt,
|
|
* drop leading data (and SYN); if nothing left, just ack.
|
|
*
|
|
+ *
|
|
+ * If the RST bit is set, check the sequence number to see
|
|
+ * if this is a valid reset segment.
|
|
+ * RFC 793 page 37:
|
|
+ * In all states except SYN-SENT, all reset (RST) segments
|
|
+ * are validated by checking their SEQ-fields. A reset is
|
|
+ * valid if its sequence number is in the window.
|
|
+ * Note: this does not take into account delayed ACKs, so
|
|
+ * we should test against last_ack_sent instead of rcv_nxt.
|
|
+ * Also, it does not make sense to allow reset segments with
|
|
+ * sequence numbers greater than last_ack_sent to be processed
|
|
+ * since these sequence numbers are just the acknowledgement
|
|
+ * numbers in our outgoing packets being echoed back at us,
|
|
+ * and these acknowledgement numbers are monotonically
|
|
+ * increasing.
|
|
+ * If we have multiple segments in flight, the intial reset
|
|
+ * segment sequence numbers will be to the left of last_ack_sent,
|
|
+ * but they will eventually catch up.
|
|
+ * In any case, it never made sense to trim reset segments to
|
|
+ * fit the receive window since RFC 1122 says:
|
|
+ * 4.2.2.12 RST Segment: RFC-793 Section 3.4
|
|
+ *
|
|
+ * A TCP SHOULD allow a received RST segment to include data.
|
|
+ *
|
|
+ * DISCUSSION
|
|
+ * It has been suggested that a RST segment could contain
|
|
+ * ASCII text that encoded and explained the cause of the
|
|
+ * RST. No standard has yet been established for such
|
|
+ * data.
|
|
+ *
|
|
+ * If the reset segment passes the sequence number test examine
|
|
+ * the state:
|
|
+ * SYN_RECEIVED STATE:
|
|
+ * If passive open, return to LISTEN state.
|
|
+ * If active open, inform user that connection was refused.
|
|
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
|
|
+ * Inform user that connection was reset, and close tcb.
|
|
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
|
|
+ * Close the tcb.
|
|
+ * TIME_WAIT state:
|
|
+ * Drop the segment - see Stevens, vol. 2, p. 964 and
|
|
+ * RFC 1337.
|
|
+ */
|
|
+ if (tiflags & TH_RST) {
|
|
+ if (tp->last_ack_sent == ti->ti_seq) {
|
|
+ switch (tp->t_state) {
|
|
+
|
|
+ case TCPS_SYN_RECEIVED:
|
|
+ so->so_error = ECONNREFUSED;
|
|
+ goto close;
|
|
+
|
|
+ case TCPS_ESTABLISHED:
|
|
+ case TCPS_FIN_WAIT_1:
|
|
+ case TCPS_FIN_WAIT_2:
|
|
+ case TCPS_CLOSE_WAIT:
|
|
+ so->so_error = ECONNRESET;
|
|
+ close:
|
|
+ tp->t_state = TCPS_CLOSED;
|
|
+ tcpstat.tcps_drops++;
|
|
+ tp = tcp_close(tp);
|
|
+ break;
|
|
+
|
|
+ case TCPS_CLOSING:
|
|
+ case TCPS_LAST_ACK:
|
|
+ tp = tcp_close(tp);
|
|
+ break;
|
|
+
|
|
+ case TCPS_TIME_WAIT:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ goto drop;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* RFC 1323 PAWS: If we have a timestamp reply on this segment
|
|
* and it's less than ts_recent, drop it.
|
|
*/
|
|
- if ((to.to_flag & TOF_TS) != 0 && (tiflags & TH_RST) == 0 &&
|
|
- tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
|
+ if ((to.to_flag & TOF_TS) != 0 && tp->ts_recent &&
|
|
+ TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
|
|
|
/* Check to see if ts_recent is over 24 days old. */
|
|
if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
|
@@ -1013,10 +1095,19 @@
|
|
* RST segments do not have to comply with this.
|
|
*/
|
|
if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) &&
|
|
- ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc) &&
|
|
- (tiflags & TH_RST) == 0)
|
|
+ ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc))
|
|
goto dropafterack;
|
|
|
|
+ /*
|
|
+ * In the SYN-RECEIVED state, validate that the packet belongs to
|
|
+ * this connection before trimming the data to fit the receive
|
|
+ * window. Check the sequence number versus IRS since we know
|
|
+ * the sequence numbers haven't wrapped. This is a partial fix
|
|
+ * for the "LAND" DoS attack.
|
|
+ */
|
|
+ if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(ti->ti_seq, tp->irs))
|
|
+ goto dropwithreset;
|
|
+
|
|
todrop = tp->rcv_nxt - ti->ti_seq;
|
|
if (todrop > 0) {
|
|
if (tiflags & TH_SYN) {
|
|
@@ -1128,40 +1219,6 @@
|
|
}
|
|
|
|
/*
|
|
- * If the RST bit is set examine the state:
|
|
- * SYN_RECEIVED STATE:
|
|
- * If passive open, return to LISTEN state.
|
|
- * If active open, inform user that connection was refused.
|
|
- * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
|
|
- * Inform user that connection was reset, and close tcb.
|
|
- * CLOSING, LAST_ACK, TIME_WAIT STATES
|
|
- * Close the tcb.
|
|
- */
|
|
- if (tiflags&TH_RST) switch (tp->t_state) {
|
|
-
|
|
- case TCPS_SYN_RECEIVED:
|
|
- so->so_error = ECONNREFUSED;
|
|
- goto close;
|
|
-
|
|
- case TCPS_ESTABLISHED:
|
|
- case TCPS_FIN_WAIT_1:
|
|
- case TCPS_FIN_WAIT_2:
|
|
- case TCPS_CLOSE_WAIT:
|
|
- so->so_error = ECONNRESET;
|
|
- close:
|
|
- tp->t_state = TCPS_CLOSED;
|
|
- tcpstat.tcps_drops++;
|
|
- tp = tcp_close(tp);
|
|
- goto drop;
|
|
-
|
|
- case TCPS_CLOSING:
|
|
- case TCPS_LAST_ACK:
|
|
- case TCPS_TIME_WAIT:
|
|
- tp = tcp_close(tp);
|
|
- goto drop;
|
|
- }
|
|
-
|
|
- /*
|
|
* If a SYN is in the window, then this is an
|
|
* error and we send an RST and drop the connection.
|
|
*/
|
|
@@ -1667,9 +1724,22 @@
|
|
/*
|
|
* Generate an ACK dropping incoming segment if it occupies
|
|
* sequence space, where the ACK reflects our state.
|
|
- */
|
|
- if (tiflags & TH_RST)
|
|
- goto drop;
|
|
+ *
|
|
+ * We can now skip the test for the RST flag since all
|
|
+ * paths to this code happen after packets containing
|
|
+ * RST have been dropped.
|
|
+ *
|
|
+ * In the SYN-RECEIVED state, don't send an ACK unless the
|
|
+ * segment we received passes the SYN-RECEIVED ACK test.
|
|
+ * If it fails send a RST. This breaks the loop in the
|
|
+ * "LAND" DoS attack, and also prevents an ACK storm
|
|
+ * between two listening ports that have been sent forged
|
|
+ * SYN segments, each with the source address of the other.
|
|
+ */
|
|
+ if (tp->t_state == TCPS_SYN_RECEIVED && (tiflags & TH_ACK) &&
|
|
+ (SEQ_GT(tp->snd_una, ti->ti_ack) ||
|
|
+ SEQ_GT(ti->ti_ack, tp->snd_max)) )
|
|
+ goto dropwithreset;
|
|
#ifdef TCPDEBUG
|
|
if (so->so_options & SO_DEBUG)
|
|
tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
|
|
|
|
Here is the patch to apply to 3.0-current systems from before
|
|
September 11, 1998. This patch is known to apply to systems just
|
|
before this date, but as you move farther back in the 3.0-current
|
|
branch, it may become more difficult for this patch to apply.
|
|
|
|
|
|
Index: tcp_input.c
|
|
===================================================================
|
|
RCS file: /home/cvsup/freebsd/CVS/src/sys/netinet/tcp_input.c,v
|
|
retrieving revision 1.80
|
|
retrieving revision 1.81
|
|
diff -u -r1.80 -r1.81
|
|
--- tcp_input.c 1998/08/24 07:47:39 1.80
|
|
+++ tcp_input.c 1998/09/11 16:04:03 1.81
|
|
@@ -979,17 +979,99 @@
|
|
|
|
/*
|
|
* States other than LISTEN or SYN_SENT.
|
|
- * First check timestamp, if present.
|
|
+ * First check the RST flag and sequence number since reset segments
|
|
+ * are exempt from the timestamp and connection count tests. This
|
|
+ * fixes a bug introduced by the Stevens, vol. 2, p. 960 bugfix
|
|
+ * below which allowed reset segments in half the sequence space
|
|
+ * to fall though and be processed (which gives forged reset
|
|
+ * segments with a random sequence number a 50 percent chance of
|
|
+ * killing a connection).
|
|
+ * Then check timestamp, if present.
|
|
* Then check the connection count, if present.
|
|
* Then check that at least some bytes of segment are within
|
|
* receive window. If segment begins before rcv_nxt,
|
|
* drop leading data (and SYN); if nothing left, just ack.
|
|
*
|
|
+ *
|
|
+ * If the RST bit is set, check the sequence number to see
|
|
+ * if this is a valid reset segment.
|
|
+ * RFC 793 page 37:
|
|
+ * In all states except SYN-SENT, all reset (RST) segments
|
|
+ * are validated by checking their SEQ-fields. A reset is
|
|
+ * valid if its sequence number is in the window.
|
|
+ * Note: this does not take into account delayed ACKs, so
|
|
+ * we should test against last_ack_sent instead of rcv_nxt.
|
|
+ * Also, it does not make sense to allow reset segments with
|
|
+ * sequence numbers greater than last_ack_sent to be processed
|
|
+ * since these sequence numbers are just the acknowledgement
|
|
+ * numbers in our outgoing packets being echoed back at us,
|
|
+ * and these acknowledgement numbers are monotonically
|
|
+ * increasing.
|
|
+ * If we have multiple segments in flight, the intial reset
|
|
+ * segment sequence numbers will be to the left of last_ack_sent,
|
|
+ * but they will eventually catch up.
|
|
+ * In any case, it never made sense to trim reset segments to
|
|
+ * fit the receive window since RFC 1122 says:
|
|
+ * 4.2.2.12 RST Segment: RFC-793 Section 3.4
|
|
+ *
|
|
+ * A TCP SHOULD allow a received RST segment to include data.
|
|
+ *
|
|
+ * DISCUSSION
|
|
+ * It has been suggested that a RST segment could contain
|
|
+ * ASCII text that encoded and explained the cause of the
|
|
+ * RST. No standard has yet been established for such
|
|
+ * data.
|
|
+ *
|
|
+ * If the reset segment passes the sequence number test examine
|
|
+ * the state:
|
|
+ * SYN_RECEIVED STATE:
|
|
+ * If passive open, return to LISTEN state.
|
|
+ * If active open, inform user that connection was refused.
|
|
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
|
|
+ * Inform user that connection was reset, and close tcb.
|
|
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
|
|
+ * Close the tcb.
|
|
+ * TIME_WAIT state:
|
|
+ * Drop the segment - see Stevens, vol. 2, p. 964 and
|
|
+ * RFC 1337.
|
|
+ */
|
|
+ if (tiflags & TH_RST) {
|
|
+ if (tp->last_ack_sent == ti->ti_seq) {
|
|
+ switch (tp->t_state) {
|
|
+
|
|
+ case TCPS_SYN_RECEIVED:
|
|
+ so->so_error = ECONNREFUSED;
|
|
+ goto close;
|
|
+
|
|
+ case TCPS_ESTABLISHED:
|
|
+ case TCPS_FIN_WAIT_1:
|
|
+ case TCPS_FIN_WAIT_2:
|
|
+ case TCPS_CLOSE_WAIT:
|
|
+ so->so_error = ECONNRESET;
|
|
+ close:
|
|
+ tp->t_state = TCPS_CLOSED;
|
|
+ tcpstat.tcps_drops++;
|
|
+ tp = tcp_close(tp);
|
|
+ break;
|
|
+
|
|
+ case TCPS_CLOSING:
|
|
+ case TCPS_LAST_ACK:
|
|
+ tp = tcp_close(tp);
|
|
+ break;
|
|
+
|
|
+ case TCPS_TIME_WAIT:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ goto drop;
|
|
+ }
|
|
+
|
|
+ /*
|
|
* RFC 1323 PAWS: If we have a timestamp reply on this segment
|
|
* and it's less than ts_recent, drop it.
|
|
*/
|
|
- if ((to.to_flag & TOF_TS) != 0 && (tiflags & TH_RST) == 0 &&
|
|
- tp->ts_recent && TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
|
+ if ((to.to_flag & TOF_TS) != 0 && tp->ts_recent &&
|
|
+ TSTMP_LT(to.to_tsval, tp->ts_recent)) {
|
|
|
|
/* Check to see if ts_recent is over 24 days old. */
|
|
if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
|
|
@@ -1020,10 +1102,19 @@
|
|
* RST segments do not have to comply with this.
|
|
*/
|
|
if ((tp->t_flags & (TF_REQ_CC|TF_RCVD_CC)) == (TF_REQ_CC|TF_RCVD_CC) &&
|
|
- ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc) &&
|
|
- (tiflags & TH_RST) == 0)
|
|
+ ((to.to_flag & TOF_CC) == 0 || tp->cc_recv != to.to_cc))
|
|
goto dropafterack;
|
|
|
|
+ /*
|
|
+ * In the SYN-RECEIVED state, validate that the packet belongs to
|
|
+ * this connection before trimming the data to fit the receive
|
|
+ * window. Check the sequence number versus IRS since we know
|
|
+ * the sequence numbers haven't wrapped. This is a partial fix
|
|
+ * for the "LAND" DoS attack.
|
|
+ */
|
|
+ if (tp->t_state == TCPS_SYN_RECEIVED && SEQ_LT(ti->ti_seq, tp->irs))
|
|
+ goto dropwithreset;
|
|
+
|
|
todrop = tp->rcv_nxt - ti->ti_seq;
|
|
if (todrop > 0) {
|
|
if (tiflags & TH_SYN) {
|
|
@@ -1135,40 +1226,6 @@
|
|
}
|
|
|
|
/*
|
|
- * If the RST bit is set examine the state:
|
|
- * SYN_RECEIVED STATE:
|
|
- * If passive open, return to LISTEN state.
|
|
- * If active open, inform user that connection was refused.
|
|
- * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
|
|
- * Inform user that connection was reset, and close tcb.
|
|
- * CLOSING, LAST_ACK, TIME_WAIT STATES
|
|
- * Close the tcb.
|
|
- */
|
|
- if (tiflags&TH_RST) switch (tp->t_state) {
|
|
-
|
|
- case TCPS_SYN_RECEIVED:
|
|
- so->so_error = ECONNREFUSED;
|
|
- goto close;
|
|
-
|
|
- case TCPS_ESTABLISHED:
|
|
- case TCPS_FIN_WAIT_1:
|
|
- case TCPS_FIN_WAIT_2:
|
|
- case TCPS_CLOSE_WAIT:
|
|
- so->so_error = ECONNRESET;
|
|
- close:
|
|
- tp->t_state = TCPS_CLOSED;
|
|
- tcpstat.tcps_drops++;
|
|
- tp = tcp_close(tp);
|
|
- goto drop;
|
|
-
|
|
- case TCPS_CLOSING:
|
|
- case TCPS_LAST_ACK:
|
|
- case TCPS_TIME_WAIT:
|
|
- tp = tcp_close(tp);
|
|
- goto drop;
|
|
- }
|
|
-
|
|
- /*
|
|
* If a SYN is in the window, then this is an
|
|
* error and we send an RST and drop the connection.
|
|
*/
|
|
@@ -1673,9 +1730,22 @@
|
|
/*
|
|
* Generate an ACK dropping incoming segment if it occupies
|
|
* sequence space, where the ACK reflects our state.
|
|
- */
|
|
- if (tiflags & TH_RST)
|
|
- goto drop;
|
|
+ *
|
|
+ * We can now skip the test for the RST flag since all
|
|
+ * paths to this code happen after packets containing
|
|
+ * RST have been dropped.
|
|
+ *
|
|
+ * In the SYN-RECEIVED state, don't send an ACK unless the
|
|
+ * segment we received passes the SYN-RECEIVED ACK test.
|
|
+ * If it fails send a RST. This breaks the loop in the
|
|
+ * "LAND" DoS attack, and also prevents an ACK storm
|
|
+ * between two listening ports that have been sent forged
|
|
+ * SYN segments, each with the source address of the other.
|
|
+ */
|
|
+ if (tp->t_state == TCPS_SYN_RECEIVED && (tiflags & TH_ACK) &&
|
|
+ (SEQ_GT(tp->snd_una, ti->ti_ack) ||
|
|
+ SEQ_GT(ti->ti_ack, tp->snd_max)) )
|
|
+ goto dropwithreset;
|
|
#ifdef TCPDEBUG
|
|
if (so->so_options & SO_DEBUG)
|
|
tcp_trace(TA_DROP, ostate, tp, &tcp_saveti, 0);
|
|
|
|
=============================================================================
|
|
FreeBSD, Inc.
|
|
|
|
Web Site: http://www.freebsd.org/
|
|
Confidential contacts: security-officer@freebsd.org
|
|
Security notifications: security-notifications@freebsd.org
|
|
Security public discussion: freebsd-security@freebsd.org
|
|
PGP Key: ftp://ftp.freebsd.org/pub/FreeBSD/CERT/public_key.asc
|
|
|
|
Notice: Any patches in this document may not apply cleanly due to
|
|
modifications caused by digital signature or mailer software.
|
|
Please reference the URL listed at the top of this document
|
|
for original copies of all patches if necessary.
|
|
=============================================================================
|
|
|
|
|
|
-----BEGIN PGP SIGNATURE-----
|
|
Version: 2.6.3ia
|
|
Charset: noconv
|
|
|
|
iQCVAwUBNiOat1UuHi5z0oilAQHd+gP/ejply8nSa1eZ4Fntvs7AI0J4+A00INa6
|
|
taew67WuQt2a6vMfjtqjYMjt09BCaxWgrKftWfb/sn9vF3WNIZ313xOf0NBpdLAm
|
|
mTctCLssy/1fw1wmeNBrrA2XyhsmiobZ6KPDOzqKR+xHF9gLQh7ygDc8dBsXUQMp
|
|
3kejs4imNb4=
|
|
=cP5N
|
|
-----END PGP SIGNATURE-----
|