Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 20 Jan 2000 19:52:57 -0800
From:      Alfred Perlstein <bright@wintelcom.net>
To:        Brett Glass <brett@lariat.org>
Cc:        security@FreeBSD.ORG
Subject:   Re: stream.c worst-case kernel paths
Message-ID:  <20000120195257.G14030@fw.wintelcom.net>
In-Reply-To: <4.2.2.20000120182425.01886ec0@localhost>; from brett@lariat.org on Thu, Jan 20, 2000 at 08:17:30PM -0700
References:  <4.2.2.20000120182425.01886ec0@localhost>

next in thread | previous in thread | raw e-mail | index | archive | help
* Brett Glass <brett@lariat.org> [000120 19:45] wrote:
> I've been browsing the code, and have seen two possible places where
> the code might be improved to lessen the impact of this DoS. Folks
> who know the stack better may know about details and side effects
> that I don't, so so if my analysis has holes in it please don't chew 
> me out TOO badly.

It's a pretty good analysis, besideds the improvements mentioned
here i _really_ think we should be able to delay the checksum as
far as possible, I've been playing with this for a bit and I'll
see how far it can be safely moved.

Doing a checksum on an invalid packet is not worth it, might as
well take the packet at face value, allow it to drop out, and
only when it's about to be accepted _finally_ take the hit and do
the checksum.

As far as limiting RST and ICMP I really believe it's time that
such things are _on_ by default.

-Alfred


> 
>  From /sys/netinet/tcp_input.c:
> 
> The kernel seeks a socket that matches the packet. It fails, of course,
> to find an open socket.
> 
>          inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport,
>              ti->ti_dst, ti->ti_dport, 1);
> 
> If the system isn't listening on the port, inp is set to NULL. But inside
> in_pcblookup_hash, we've had to do two hash table lookups, because
> the "wildcard" flag is set to 1. (Suggested improvement: turn off the
> wildcard search if the packet is not a SYN. I suppose that if the
> packet IS a SYN, we still have to test to see if it was erroneously
> sent in the middle of a connection, so we really have to do both tests 
> in that case. See in_pcb.c.)
> 
> Back to tcp_input.c. We then execute the following:
> 
>          if (inp == NULL) {
>                  if (log_in_vain && tiflags & TH_SYN) {
>                          char buf[4*sizeof "123"];
> 
>                          strcpy(buf, inet_ntoa(ti->ti_dst));
>                          log(LOG_INFO,
>                              "Connection attempt to TCP %s:%d from %s:%d\n",
>                              buf, ntohs(ti->ti_dport), inet_ntoa(ti->ti_src),
>                              ntohs(ti->ti_sport));
>                  }
>                  }
> #ifdef ICMP_BANDLIM
>                  if (badport_bandlim(1) < 0)
>                          goto drop;
> #endif
>                  goto dropwithreset;
>          }
> 
> Normally, we'll wind up at the label "dropwithreset", which means we'll send back a RST.
> This suggests that restricting RSTs will help with the DoS. (Does anyone know if
> not sending an RST violates any RFCs if there was never a connection?)
> 
> Trouble is, a smart attacker will fire the barrage at a port on which the machine is
> listening. This makes the code path longer because the hash table lookup will succeed.
> We execute:
> 
>          tp = intotcpcb(inp);
>          if (tp == 0)
>                  goto dropwithreset;
>          if (tp->t_state == TCPS_CLOSED)
>                  goto drop;
> 
>          /* Unscale the window into a 32-bit value. */
>          if ((tiflags & TH_SYN) == 0)
>                  tiwin = ti->ti_win << tp->snd_scale;
>          else
>                  tiwin = ti->ti_win;
> 
>          so = inp->inp_socket;
>          if (so->so_options & (SO_DEBUG|SO_ACCEPTCONN)) {
> #ifdef TCPDEBUG
>                  if (so->so_options & SO_DEBUG) {
>                          ostate = tp->t_state;
>                          tcp_saveti = *ti;
>                  }
> #endif
>                  if (so->so_options & SO_ACCEPTCONN) {
>                          register struct tcpcb *tp0 = tp;
>                          struct socket *so2;
>                          if ((tiflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN) {
>                                  /*
>                                   * Note: dropwithreset makes sure we don't
>                                   * send a RST in response to a RST.
>                                   */
>                                  if (tiflags & TH_ACK) {
>                                          tcpstat.tcps_badsyn++;
>                                          goto dropwithreset;
>                                  }
>                                  goto drop;
> 
> At which point the packet is dropped, since tiflags & (TH_RST|TH_ACK|TH_SYN)) != TH_SYN
> (that is, it's not a SYN). But we've had to attempt two hash table lookups (we could have done 
> only one if we'd tested for a SYN) and send the RST unless we've disabled it. Worse still, if 
> we send the RSTs, we may have to handle an ICMP "unreachable" message from the router if the 
> source address is spoofed.
> 
> So, the two obvious optimizations are to add a test for SYNs prior to the hash table lookup and
> not to send a RST back.
> 
> --Brett
> 
> 
> 
> 
> 
> To Unsubscribe: send mail to majordomo@FreeBSD.org
> with "unsubscribe freebsd-security" in the body of the message

-- 
-Alfred Perlstein - [bright@wintelcom.net|alfred@freebsd.org]


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20000120195257.G14030>