DNS+scapy学习

DNS前置知识

大部分介绍转自这篇文章
官方解释: DNS ( Domain Name System ,域名系统) ,因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。
通过主机名,最终得到该主机名对应的IP地址的过程叫做域名解析(或主机名解析)。
通俗的讲,我们更习惯于记住一个网站的名字,比如www.baidu.com,而不是记住它的ip地址(110.242.68.3)。然而计算机网络通信中所识别的标识并不是域名,而是IP地址,因为其可以提供主机在互联网中的位置信息,而且是定长的,路由器等设备更容易处理。为了折中人类和计算机不同的偏好,就出现了 DNS(DomainNameSystem,域名系统),其主要任务是根据域名查出对应的 IP 地址。

域名

域名由若干个英文字符串组成(不区分大小写),各字符串之间用点号『.』分隔连接,其中越靠右的表示域名级别越高。
举个例子,百度的域名为http://www.baidu.com,其中com是顶级域名(一级域名),baidu是二级域名,www是三级域名。

域名服务器

域名服务器(也称为 DNS 服务器)负责存储域名和 IP 地址的映射关系,当我们需要获取某个域名对应的 IP 地址时,只需要从域名服务器中查询即可。

由于域名非常非常多,如果都存放在一台域名服务器中,那么不仅查询速度慢,服务器压力大,而且难以保证服务的可靠性。因此,DNS 采用了分布式的设计方案,大量的域名服务器之间通过层次方式组织,分布在全世界范围内。
分布式是指将一个系统或任务的多个部分分散到多个独立节点上,这些节点通过网络协作共同完成整体任务。分布式系统的核心特点是去中心化、可扩展性和容错性,能够通过多个节点的并行处理来提高效率、可靠性和可用性。

DNS是一个典型的分布式系统,它将全球域名解析的任务分散到多个服务器上,而不是依赖单一的中心化服务器。DNS采用树状层级结构,将域名划分为多个层级(如根域、顶级域、二级域等、三级域等)。
一般而言,域名服务器可以分为以下四类:

根域名服务器:最高层级的域名服务器,因特网上一共有 13 个根域名服务器(以英文字母 A 到 M 依序命名,格式为[a~m].root-servers.net),每个根域名服务器都知道所有顶级域名服务器的 IP 地址,比如知道负责 com 域的顶级域名服务器的 IP 地址。

顶级域名服务器:对于每个顶级域名,如 com、org、edu 等,都有对应的顶级域名服务器。顶级域名服务器知道其所管理的所有权威域名服务器的 IP 地址,比如负责 com 域的顶级域名服务器知道负责 http://baidu.com 域的权威域名服务器的 IP 地址。

权威域名服务器:一个网站需要将其域名和 IP 地址注册到相应的权威域名服务器中,比如网站www.baidu.com的域名和IP地址就存储在负责baidu.com域的权威域名服务器中。

本地域名服务器:本地域名服务器不属于上述域名服务器的层次结构,但是它对域名系统非常重要。每个 ISP(如一个大学、一个公司)都有一个本地域名服务器(也叫默认域名服务器)。

域名解析流程

知道了域名和域名服务器的基础知识后,我们来了解一下域名解析的具体流程,以输入百度域名为例,看看我们的主机是如何得到http://www.baidu.com的 IP 地址的。

请求主机向本地域名服务器发送 DNS 查询报文,询问http://www.baidu.com的 IP 地址是什么;

本地域名服务器转发此查询报文到根域名服务器;

根域名服务器发现要查询的顶级域名为com,于是向本地域名服务器发送响应报文,报文中封装了负责com域的顶级域名服务器的 IP 地址列表;

本地域名服务器收到根域名服务器响应的报文后,选择其中一个顶级域名服务器的 IP 地址,并向其发送查询报文;

顶级域名服务器发现要查询的二级域名为baidu,于是向本地域名服务器发送响应报文,报文中封装了负责http://baidu.com域的权威域名服务器的 IP 地址列表;

本地域名服务器收到顶级域名服务器响应的报文后,选择其中一个权威域名服务器的 IP 地址,并向其发送查询报文;

权威域名服务器通过查询数据库,找到http://www.baidau.com的 IP 地址,并将此信息封装为一个响应报文,发送给本地域名服务器;

本地域名服务器将响应报文发送给原请求主机。我们的主机就知道了百度的 IP 地址,DNS 查询过程结束。
现在当一台设备要访问互联网上的某个网站时,它会首先向本地DNS服务器请求(或者手工设置,或者从DHCP服务器获得)这个网站的地址。本地DNS服务器先检查一下自已的缓存中有没有这个IP地址,有的话就直接返回。如果缓存中没有对应的IP地址,就要进行递归查询。

DNS缓存

DNS缓存是一种临时存储域名解析结果的机制,旨在提高域名解析的速度和效率。当用户首次访问某个网站时,DNS系统会将该网站的域名解析为对应的IP地址,并将这一结果存储在本地或DNS服务器的缓存中。在缓存的有效期内,如果用户再次访问该网站,系统可以直接从缓存中获取IP地址,而无需再次进行完整的DNS查询过程,从而显著减少解析时间

DNS缓存的工作原理

  1. 浏览器缓存:当用户输入一个域名时,浏览器首先检查自己的缓存中是否有该域名对应的IP地址。如果有且未过期,则直接使用缓存结果
  2. 系统缓存:如果浏览器缓存中没有找到,操作系统会检查其缓存(如Windows的hosts文件)中是否有解析结果
  3. 路由器缓存:若系统缓存也未命中,查询会发送到路由器,检查其缓存
  4. ISP DNS缓存:如果以上缓存均未找到,请求会被发送到互联网服务提供商(ISP)的DNS服务器,检查其缓存
  5. 根域名服务器和各级域名服务器:如果ISP的DNS缓存也未命中,查询会依次向根域名服务器、顶级域名服务器和权威域名服务器发起,直到找到正确的IP地址
  6. 缓存结果:最终,解析结果会被存储在本地DNS服务器和客户端缓存中,并设置一个有效期(TTL),以便后续使用

DNS缓存的作用

加速访问:通过减少重复的DNS查询,DNS缓存显著提高了网页加载速度
减轻服务器负担:缓存机制减少了DNS服务器的查询压力,提高了系统的整体性能

DNS缓存的潜在问题

数据过期:如果域名的IP地址发生变化,而缓存未及时更新,用户可能会访问到错误的IP地址
安全风险:DNS缓存可能被恶意攻击者利用,进行缓存投毒攻击,导致用户被重定向到恶意网站

管理DNS缓存

为了确保DNS缓存的有效性和安全性,可以采取以下措施:
设置合理的TTL值:TTL(Time to Live)决定了缓存记录的有效期。过长的TTL可能导致数据过期,而过短的TTL会增加服务器负担
定期清理缓存:手动或自动清理DNS缓存可以避免因缓存过期或污染导致的问题
通过合理管理和使用DNS缓存,可以在提升网络性能的同时,降低潜在的安全风险

在小蓝本中搜索公司的时候,可以看到公司的域名(如果有),一些公司域名点击后跳转到广告网站、色情网站等和公司毫不相干的网站,可能就是DNS劫持、DNS缓存污染、域名解析被篡改、域名过期或被抢注等原因导致的。

DNS报文格式

完整版看这位大佬https://blog.csdn.net/weixin_45975575/article/details/115561913

from scapy.all import *
from scapy.layers.dhcp import DHCP
ls(DNS())

"""
输出
length     : ShortField (Cond)                   = None            ('None')
id         : ShortField                          = 0               ('0')
qr         : BitField  (1 bit)                   = 0               ('0')
opcode     : BitEnumField                        = 0               ('0')
aa         : BitField  (1 bit)                   = 0               ('0')
tc         : BitField  (1 bit)                   = 0               ('0')
rd         : BitField  (1 bit)                   = 1               ('1')
ra         : BitField  (1 bit)                   = 0               ('0')
z          : BitField  (1 bit)                   = 0               ('0')
ad         : BitField  (1 bit)                   = 0               ('0')
cd         : BitField  (1 bit)                   = 0               ('0')
rcode      : BitEnumField                        = 0               ('0')
qdcount    : FieldLenField                       = None            ('None')
ancount    : FieldLenField                       = None            ('None')
nscount    : FieldLenField                       = None            ('None')
arcount    : FieldLenField                       = None            ('None')
qd         : _DNSPacketListField                 = [<DNSQR  |>]    ('[<DNSQR  |>]')
an         : _DNSPacketListField                 = []              ('[]')
ns         : _DNSPacketListField                 = []              ('[]')
ar         : _DNSPacketListField                 = []              ('[]')
"""

这段返回内容解析了 DNS() 包的字段:

  1. length 和 idlengthShortField,通常与条件相关,但默认是 Noneid 表示 DNS 报文的唯一标识符,默认为 0

  2. 标志字段(Flags)

    • qr:查询/响应标志,0 表示查询,1 表示响应。
    • opcode:操作码,用于指示请求的类型,0 表示标准查询。
    • aa:权威回答标志,0 表示非权威响应。
    • tc:截断标志,报文比允许长度还长就截断,0 表示报文未截断。
    • rd:期望递归标志,1 表示客户端希望服务器进行递归查询。
    • ra:递归可用标志,0 表示服务器不支持递归。
    • zadcd:保留位和认证数据标志,默认为 0
    • rcode:返回码,0 表示无错误。
  3. 计数字段

    • qdcountancountnscountarcount:分别为问题、回答、授权和附加资源记录的数量,默认为 None
  4. 资源记录列表

    • qd:包含 DNSQR 对象,表示问题记录。
    • annsar:分别表示回答、授权和附加记录,默认是空列表 []

这段信息显示了 DNS() 的各个字段,适用于 DNS 查询和响应的构建与解析。

ls(DNSQR())

"""
qname      : DNSStrField                         = b'www.example.com.' ("b'www.example.com.'")
qtype      : ShortEnumField                      = 1               ('1')
unicastresponse : BitField  (1 bit)                   = 0               ('0')
qclass     : BitEnumField                        = 1               ('1')
"""

以下是DNSQR()字段的含义:

  1. qname:DNS 查询名称字段,用于指定查询的域名。例如 "www.example.com"。该字段类型为 DNSStrField,表示为一个字符串字段。

  2. qtype:查询类型字段,使用 ShortEnumField 类型。值为 1 表示查询的类型是 A 记录,即请求主机的 IPv4 地址。其他常见值包括 5(CNAME 记录)和 15(MX 记录)。

  3. unicastresponse:单播响应字段,1 位布尔值。默认为 0 表示不要求单播响应,DNS 响应可以广播。

  4. qclass:查询类字段,值 1 表示 IN 类,即互联网类。这个字段通常固定为 IN,用于表示查询的资源类型。

这些字段共同描述了 DNS 查询请求中的内容,帮助 DNS 服务器理解客户端所请求的资源类型和目标域名。

DNS安全

DNS泛洪攻击

转自https://zhuanlan.zhihu.com/p/103069432

DNS泛洪攻击是什么?
DNS泛洪是一种分布式拒绝服务(DDoS)攻击,其中攻击者瞄准属于给定区域的一个或多个域名系统(DNS)服务器,试图阻碍该区域及其子区域的资源记录的解析。
DNS服务器是互联网的“路线图”,帮助请求者找到他们寻求的服务器。DNS区域是域名系统(DNS)中域名空间的不同部分。对于每个区域,管理职责被委派给单个服务器集群。
在DNS泛洪攻击中,犯罪者试图用明显有效的流量来覆盖给定的DNS服务器(或多个服务器),压倒服务器资源并阻碍服务器将合法请求定向到区域资源的能力。

DNS泛洪攻击攻击描述
DNS泛洪攻击应与DNS放大攻击明确区分开来。DNS放大是一种不对称的DDoS攻击,攻击者通过欺骗性目标IP发出一个小的查询查询,使得欺骗目标成为更大的DNS响应的接收者。通过这些攻击,攻击者的目标是通过不断耗尽带宽容量来使网络饱和。
DNS泛洪是对称的DDoS攻击。这些攻击试图通过大量UDP请求耗尽服务器端资产(例如,内存或CPU),这些UDP请求由在多个受损僵尸网络机器上运行的脚本生成。
DNS泛洪攻击被认为是UDP泛洪攻击的变种,因为DNS服务器依赖UDP协议进行名称解析,并且是第7层攻击。使用基于UDP的查询(与TCP查询不同),永远不会建立完整的电路,因此更容易实现欺骗。

DNS放大攻击

参考https://www.cnblogs.com/Higgerw/p/15123176.html
曾经最流行的放大攻击协议是DNS放大攻击: 一个查找域名IP地址的小DNS查询将导致一个大的应答。DNS查询通常通过UDP协议发送。UDP是一种“发即忘”协议,这意味着不需要通过握手来验证数据包IP地址的真实性。这意味着,攻击者可以伪造UDP数据包的报头,以表明它来自您想要攻击的特定IP,并将这个伪造的数据包发送到一个开放的DNS解析器。DNS解析器将回复一个对伪造IP地址的响应,并回答所问的任何问题(发送一系列大数据包)。
一个好的放大攻击载体有两个标准:

  1. 查询可以设置一个欺骗源地址(例如,通过不需要握手的ICMP或UDP协议);
  2. 对查询的响应明显大于查询本身。
    DNS是满足这些条件的核心、无处不在的互联网平台,因此已成为放大攻击的最大来源。产生根本原因是,许多运行DNS解析器的网络运营商将DNS解析器打开,并愿意响应任何查询他们的IP地址。最近发生的情况是,一些不同的僵尸网络似乎列举了互联网的IP空间,以发现开放的解析器。一旦发现,它们可以用来发动重大的DNS放大攻击。
解决方案
1.如果运行递归DNS解析器,最佳实践是确保它只响应来自授权客户端的查询。换句话说,如果你正在为你的公司运行一个递归DNS服务器,并且你的公司的IP空间是5.5.5.0/24(即5.5.5.0 - 5.5.5.255),那么它应该只响应来自这个范围的查询。如果一个查询从9.9.9.9到达,那么它不应该响应。
2.禁用DNS服务递归。
3.假设我们知道我们没有从我们的网络发送任何DNS查询。因此,我们可以安全地过滤来自DNS解析器的响应:在我们的路由器上丢弃来自开放解析器的响应

DNS欺骗

转自
DNS欺骗就是利用了DNS协议设计时的一个非常严重的安全缺陷。

首先欺骗者向目标机器发送构造好的ARP应答数据包(关于ARP欺骗请看这位大佬的文章https://www.cnblogs.com/ichunqiu/p/5662832.html),ARP欺骗成功后,嗅探到对方发出的DNS请求数据包,分析数据包取得ID和端口号后,向目标发送自己构造好的一个DNS返回包,对方收到DNS应答包后,发现ID和端口号全部正确,即把返回数据包中的域名和对应的IP地址保存进DNS缓存表中,而后来的当真实的DNS应答包返回时则被丢弃。

假设嗅探到目标靶机发出的DNS请求包有以下内容:

Source address : 192.168.1.57
Destination address : ns.baidu.com
Source port : 1234
Destination port : 53 (DNS port)
Data : www.baidu.com

我们伪造的DNS应答包如下:

Source address : ns.baidu.com
Destination address : 192.168.1.57
Source port : 53 (DNS port)
Destination port : 1234
Data : www.baidu.com 192.168.1.59
目标靶机收到应答包后把域名以及对应IP保存在了DNS缓存表中,这样www.baidu.com的地址就被指向到了192.168.1.59上。

dns欺骗一般要结合中间人攻击完成
dns请求一般直接到达dns服务器或者经过网关到达dns服务器,攻击者要冒充网关

模拟dns欺骗

这两个工具可能要自己安装
kali开俩终端,开启apache服务
第一个输入arpspoof -i eth0 -t 靶机ip 网关ip

第二个输入arpspoof -i eth0 -t 网关ip 靶机ip

编辑一个hosts.txt里面写攻击机IP 要欺骗的网站如192.168.xx.xxx www.baidu.com
开第三个终端输入dnsspoof -i eth0 -f hosts.txt

靶机ping或curl www.baidu.com,返回的是攻击者ip

隐私泄露

DNS查询消息可能被恶意第三方截获,暴露用户的浏览习惯和访问记录,导致隐私泄露。虽然DNSSEC可以保护DNS响应的完整性,但查询消息本身仍然可能被监听

钓鱼攻击

攻击者注册与合法域名相似的仿冒域名(如“www.badu.com”),用于实施钓鱼攻击,诱骗用户输入敏感信息或下载恶意软件.

SCAPY模拟DNS查询

from scapy.all import *

from scapy.layers.dns import DNSQR

pkt=IP(dst='202.115.39.9')/UDP(dport=53)/DNS(id=21090,qr=0,opcode=0,rd=1,qd=DNSQR(qname='www.163.com'))
dns_res=sr1(pkt)
ls(dns_res)
loop=1
while 1:
    try:
        dns_res_ip=dns_res.getlayer(DNS).fields['an'][loop].fields['rdata']
        print(dns_res_ip)
        loop+=1
    except:
        break
 

IP(dst='202.115.39.9')----------设置dns服务器地址,自己打开终端ipconfig/all查看
UDP(dport=53)---------dns协议使用53号端口
DNS(id=21090,qr=0,opcode=0,rd=1,qd=DNSQR(qname='www.163.com'))

id='21090':DNS 报文的标识符,用于客户端与服务器之间的查询关联。
qr=0:查询/响应标志,值为 0 表示该数据包是 DNS 查询请求。
opcode=0:操作码,值为 0 表示标准查询(通常用于主机名解析)。
rd=1:递归查询标志,值为 1 表示客户端请求服务器执行递归查询。
qd=DNSQR(qname='www.163.com'):包含一个 DNS 查询记录,查询的域名为 www.163.com。

运行结果

Begin emission
..
Finished sending 1 packets
.*
Received 4 packets, got 1 answers, remaining 0 packets
version    : BitField  (4 bits)                  = 4               ('4')
ihl        : BitField  (4 bits)                  = 5               ('None')
tos        : XByteField                          = 0               ('0')
len        : ShortField                          = 258             ('None')
id         : ShortField                          = 13940           ('1')
flags      : FlagsField                          = <Flag 0 ()>     ('<Flag 0 ()>')
frag       : BitField  (13 bits)                 = 0               ('0')
ttl        : ByteField                           = 61              ('64')
proto      : ByteEnumField                       = 17              ('0')
chksum     : XShortField                         = 33491           ('None')
src        : SourceIPField                       = '202.115.39.9'  ('None')
dst        : DestIPField                         = '10.134.199.161' ('None')
options    : PacketListField                     = []              ('[]')
--
sport      : ShortEnumField                      = 53              ('53')
dport      : ShortEnumField                      = 53              ('53')
len        : ShortField                          = 238             ('None')
chksum     : XShortField                         = 62902           ('None')
--
length     : ShortField (Cond)                   = None            ('None')
id         : ShortField                          = 21090           ('0')
qr         : BitField  (1 bit)                   = 1               ('0')
opcode     : BitEnumField                        = 0               ('0')
aa         : BitField  (1 bit)                   = 0               ('0')
tc         : BitField  (1 bit)                   = 0               ('0')
rd         : BitField  (1 bit)                   = 1               ('1')
ra         : BitField  (1 bit)                   = 1               ('0')
z          : BitField  (1 bit)                   = 0               ('0')
ad         : BitField  (1 bit)                   = 0               ('0')
cd         : BitField  (1 bit)                   = 0               ('0')
rcode      : BitEnumField                        = 0               ('0')
qdcount    : FieldLenField                       = 1               ('None')
ancount    : FieldLenField                       = 10              ('None')
nscount    : FieldLenField                       = 0               ('None')
arcount    : FieldLenField                       = 0               ('None')
qd         : _DNSPacketListField                 = [<DNSQR  qname=b'www.163.com.' qtype=A unicastresponse=0 qclass=IN |>] ('[<DNSQR  |>]')
an         : _DNSPacketListField                 = [<DNSRR  rrname=b'www.163.com.' type=CNAME cacheflush=0 rclass=IN ttl=225 rdata=b'www.163.com.163jiasu.com.' |>, <DNSRR  rrname=b'www.163.com.163jiasu.com.' type=CNAME cacheflush=0 rclass=IN ttl=218 rdata=b'www.163.com.w.kunluncan.com.' |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.237 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.231 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.233 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.232 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.238 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.236 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.235 |>, <DNSRR  rrname=b'www.163.com.w.kunluncan.com.' type=A cacheflush=0 rclass=IN ttl=21 rdata=119.6.222.234 |>] ('[]')
ns         : _DNSPacketListField                 = []              ('[]')
ar         : _DNSPacketListField                 = []              ('[]')
b'www.163.com.w.kunluncan.com.'
119.6.222.237
119.6.222.231
119.6.222.233
119.6.222.232
119.6.222.238
119.6.222.236
119.6.222.235
119.6.222.234

当然有更加方便的DNS查询库

from dns import resolver
ans = resolver.resolve("www.baidu.com", "A")
for i in ans.response.answer:
    print(i)

加密dns协议

from dns_over_https import SecureDNS
r = SecureDNS()
print(r.gethostbyname('www.baidu.com'))

防御对策

为了防止针对DNS系统的攻击,强化域名系统的安全性,互联网诞生了4种提升DNS安全性的协议,分别是DNSSEC,DNSCrypt,DNS over TLS(DoT),DNS over HTTPS(DoH),DoQ (DNS over QUIC)。

DoT利用传输层安全性协议(TLS)来加密DNS查询,确保数据在传输过程中不被监听或篡改。DoT通常在853端口上运行,并提供了一种端到端加密的解决方案。
DoH是一种通过HTTPS协议传输DNS查询的方法。它利用了HTTPS协议天然的加密特性来保障数据传输的隐私性和安全性。DoH的引入使DNS查询与普通的WEB流量混合,从而更难被封锁或监控。
DoQ是一种新兴的技术,它结合了DNS查询和QUIC协议的优势。QUIC是一个基于UDP的多路复用传输协议,它减少了连接建立时间,并提供了更好的性能和加密支持。DoQ旨在进一步提升DNS查询的效率和隐私性。

(来自https://www.dnswiki.cn/index.php?title=DoH,_DoT,DoQ%E7%9A%84%E5%8C%BA%E5%88%AB)

posted @ 2025-03-21 10:56  积分别忘C  阅读(88)  评论(0)    收藏  举报