[2313] in bugtraq
Re: denial of service attack possible
daemon@ATHENA.MIT.EDU (Andrew Gross)
Fri Oct 27 17:13:09 1995
Date: Fri, 27 Oct 1995 09:52:31 PDT
Reply-To: Bugtraq List <BUGTRAQ@CRIMELAB.COM>
From: Andrew Gross <grossa@SDSC.EDU>
X-To: Mark@MISTY.COM
To: Multiple recipients of list BUGTRAQ <BUGTRAQ@CRIMELAB.COM>
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