Bugtraq mailing list archives

Re: denial of service attack possible


From: grossa () SDSC EDU (Andrew Gross)
Date: Fri, 27 Oct 1995 09:52:31 PDT


Hello,

I posted this to sun-managers, but it has some nasty consequences if deliberately
exploited.  If anyone has any more info, or ideas for a fix, please let me know.

   The internet draft draft-heavens-problems-rsts-00.txt describes the
problem in detail.  In short, TCP doesn't handle RSTs correctly during
the open and close negotiations of a connection.


It concerns me that one remote site can so easily completely block all
incoming tcp/ip connections on a port.  Is this a kernel bug, or something
I can take some measure to prevent on this end?

   TCP is a second generation protocol -- it's buggy.  This is but one of
several such problems.


If anyone has any more specifics on this problem, please let me know.  When
the server is healthy netstat indicates a couple SYN_RCVD state services, but
they never last from one netstat command to another for the same remote IP.

   I'm including a program that I use to clean out these "stuck"
connections.  It also keeps us from having to reboot our web server
once a week.  One caveat is that I haven't seen a SYN_RCVD connection
stuck, so your mileage may vary on that one.  The others work like a
charm.


Andrew Gross
--
Andrew Gross (grossa () sdsc edu) |  Information  |         Voice: +1.619.534.5086
San Diego Supercomputer Center |  Security     |           FAX: +1.619.534.5152
P. O. Box 85608                |  Researcher   | Quis custodiet ipsos custodes.
San Diego CA 92186-9784        |               | --Juvenal, _Satires_, VI, 347
=========================================================================
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#       socket_clear.c
# This archive created: Fri Oct 27 09:48:53 1995
export PATH; PATH=/bin:$PATH
if test -f 'socket_clear.c'
then
        echo shar: will not over-write existing file "'socket_clear.c'"
else
cat << \SHAR_EOF > 'socket_clear.c'
/*
 * Andrew Gross 30 Sep 1995 (grossa () sdsc edu) [SunOS 4.1.4]
 *
 * This program will help out on WWW and anon-ftp servers by clearing
 * out "stuck" sockets.
 *
 * This program when compiled as (cc .c -lkvm) prints out information
 * on sockets in the closing states.
 *
 * When compiled as (cc .c -DFIX -lkvm), it goes and "helps out"
 * sockets stuck in closing states.  Sockets in CLOSE_WAIT, LAST_ACK,
 * FIN_WAIT_1, and CLOSING with the TCPT_REXMT and TCPT_2MSL timers off are
 * moved into TIME_WAIT and the timers TCPT_PERSIST and TCPT_KEEP are
 * cleared.  The concern here is that soisdisconnected() get called to
 * clear out a possible waiting process and this happens correctly
 * when the TCPT_2MSL timer goes off.
 *
 *
 * These are the differences on a WWW server from a netstat -m before (<)
 * and after (>) running the fixing version of this code.
 *
 *   < 2191/3648 mbufs in use:
 *   <       923 mbufs allocated to data
 *   <       119 mbufs allocated to packet headers
 *   <       244 mbufs allocated to socket structures
 *   <       387 mbufs allocated to protocol control blocks
 *   ---
 *   > 1445/3648 mbufs in use:
 *   >       360 mbufs allocated to data
 *   >       72 mbufs allocated to packet headers
 *   >       202 mbufs allocated to socket structures
 *   >       293 mbufs allocated to protocol control blocks
 *
 *   < 474/592 cluster buffers in use
 *   < 1048 Kbytes allocated to network (71% in use)
 *   ---
 *   > 157/368 cluster buffers in use
 *   > 824 Kbytes allocated to network (40% in use)
 *
 * 20 October 1995
 * The internet draft draft-heavens-problems-rsts-00.txt describes the
 * problem that this code combats.
 *
 */

#include <stdio.h>
#include <nlist.h>
#include <kvm.h>
#include <sys/types.h>

#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_pcb.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_seq.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcp_debug.h>


struct nlist nl[] = {
#define N_TCB           0
        { "_tcb" },
        "",
};

struct inpcb pcb;
struct tcpcb tcb;


main()
{
   kvm_t *kd;
   struct inpcb *curr,*prev=NULL,*next,*orig;
   struct socket soc;
   int i;


   if(!(kd = kvm_open(NULL, NULL, NULL, O_RDWR, NULL))) {
      perror("kvm_open");
      exit(1);
   }

   if(kvm_nlist(kd, nl)) {
      perror("kvm_nlist");
      exit(1);
   }

   curr = orig = (struct inpcb *) nl[N_TCB].n_value;

   do {
      if((kvm_read(kd, (u_long) curr, &pcb, sizeof pcb) != sizeof pcb)
         || (prev && (prev != pcb.inp_prev))) {
         perror("read current");
         exit(1);
      }

      if(!pcb.inp_ppcb)
         continue;

      if((kvm_read(kd, (u_long) pcb.inp_ppcb, &tcb, sizeof tcb)
         != sizeof tcb) || (curr != tcb.t_inpcb)) {
         perror("read tcpcb");
         exit(1);
      }

#ifndef FIX
      if((kvm_read(kd, (u_long) pcb.inp_socket, &soc, sizeof soc)!= sizeof soc)
         || (curr != (struct inpcb *) soc.so_pcb)) {
         perror("read soc");
         exit(1);
      }
#endif

/* only deal with closing... */

/*
 * T2&& (possibly) T1 && (close_wait||last_ack||closing||fin_wait_1)
 */

#ifdef FIX
      if ( tcb.t_state != SYN_RCVD &&
           tcb.t_state != TCPS_LAST_ACK &&
           tcb.t_state != TCPS_CLOSE_WAIT &&
           tcb.t_state != TCPS_FIN_WAIT_1 &&
           tcb.t_state != TCPS_CLOSING )
         continue;

      if ( tcb.t_timer[TCPT_REXMT] !=0 ||
           tcb.t_timer[TCPT_2MSL] !=0 )
         continue;
#else
      /* skip uninteresting states: up to est. and normal finish */
      if ( tcb.t_state <= TCPS_ESTABLISHED ||
           tcb.t_state == TCPS_TIME_WAIT && tcb.t_timer[TCPT_2MSL] != 0)
         continue;
#endif

      printf("s=%s %d ", inet_ntoa(pcb.inp_laddr), pcb.inp_lport);
      printf("d=%s %d ",  inet_ntoa(pcb.inp_faddr), pcb.inp_fport);

      if (tcb.t_state < 0 || tcb.t_state >= TCP_NSTATES)
         printf("st=%d", tcb.t_state);
      else
         printf("st=%s", tcpstates[tcb.t_state]);

#ifndef FIX
      printf(" so_st=%d ",soc.so_state);
/*
      printf("ka=%d ",soc.so_state&SO_KEEPALIVE);
      printf(" ling=%d ",soc.so_state&SO_LINGER);
*/
      printf("wupalt=%p",soc.so_wupalt);
#endif

      printf("\n");

      for (i=0; i<TCPT_NTIMERS; i++) printf("T(%d)= %5d ",i,tcb.t_timer[i]);
      printf("\n");

#ifdef FIX
      if ( tcb.t_timer[TCPT_KEEP] > 3 && ( tcb.t_timer[TCPT_PERSIST] == 0 ||
           tcb.t_timer[TCPT_PERSIST] > 3 ) ) {
         /*
          * Get out of wait free:
          * Since tcp_close calls soisdisconnected we can do this
          * as that call is need to transition to CLOSE_WAIT .
          *
          */
         tcb.t_state = TCPS_TIME_WAIT;
         tcb.t_timer[TCPT_PERSIST] = 0;
         tcb.t_timer[TCPT_KEEP] = 0;
         tcb.t_timer[TCPT_2MSL] = 10;

         if((kvm_write(kd, (u_long) pcb.inp_ppcb, &tcb, sizeof tcb)
            != sizeof tcb) ) {
            perror("kvm_write");
            exit(1);
         }
      }
#endif

   } while ( prev = curr, curr = pcb.inp_next, curr != orig );

}
SHAR_EOF
fi # end of overwriting check
#       End of shell archive
exit 0



Current thread: