https://ephen.me/2017/dns-tcp/
https://c.biancheng.net/view/6457.html
https://www.jianshu.com/p/b483300378af
https://www.cnblogs.com/549294286/p/5172448.html
wireshark数据包分析
Packet Details Pane(数据包详细信息), 在数据包列表中选择指定数据包,在数据包详细信息中会显示数据包的所有详细信息内容。数据包详细信息面板是最重要的,用来查看协议中的每一个字段。各行信息分别为
(1)Frame: 物理层的数据帧概况
(2)Ethernet II: 数据链路层以太网帧头部信息
(3)Internet Protocol Version 4: 互联网层IP包头部信息
(4)User Datagram Protocol: 传输层的数据段头部信息,此处是UDP
(5)Domain Name System : 应用层的信息,此处是DNS协议
请求包
响应包
DNS数据包格式
事务 ID、标志、问题计数、回答资源记录数、权威名称服务器计数、附加资源记录数这 6 个字段是DNS的报文首部,共 12 个字节。
整个 DNS 格式主要分为 3 部分内容,即基础结构部分、问题部分、资源记录部分。
事务 ID:DNS 报文的 ID 标识。对于请求报文和其对应的应答报文,该字段的值是相同的。通过它可以区分 DNS 应答报文是对哪个请求进行响应的。
标志:DNS 报文中的标志字段。
问题计数:DNS 查询请求的数目。
回答资源记录数:DNS 响应的数目。
权威名称服务器计数:权威名称服务器的数目。
附加资源记录数:额外的记录数目(权威名称服务器对应 IP 地址的数目)
标志字段又分为若干个字段
标志字段中每个字段的含义如下:
QR(Response):查询请求/响应的标志信息。查询请求时,值为 0;响应时,值为 1。
Opcode:操作码。其中,0 表示标准查询;1 表示反向查询;2 表示服务器状态请求。
AA(Authoritative):授权应答,该字段在响应报文中有效。值为 1 时,表示名称服务器是权威服务器;值为 0 时,表示不是权威服务器。
TC(Truncated):表示是否被截断。值为 1 时,表示响应已超过 512 字节并已被截断,只返回前 512 个字节。
RD(Recursion Desired):期望递归。该字段能在一个查询中设置,并在响应中返回。该标志告诉名称服务器必须处理这个查询,这种方式被称为一个递归查询。如果该位为 0,且被请求的名称服务器没有一个授权回答,它将返回一个能解答该查询的其他名称服务器列表。这种方式被称为迭代查询。
RA(Recursion Available):可用递归。该字段只出现在响应报文中。当值为 1 时,表示服务器支持递归查询。
Z:保留字段,在所有的请求和应答报文中,它的值必须为 0。
rcode(Reply code):返回码字段,表示响应的差错状态。当值为 0 时,表示没有错误;当值为 1 时,表示报文格式错误(Format error),服务器不能理解请求的报文;当值为 2 时,表示域名服务器失败(Server failure),因为服务器的原因导致没办法处理这个请求;当值为 3 时,表示名字错误(Name Error),只有对授权域名解析服务器有意义,指出解析的域名不存在;当值为 4 时,表示查询类型不支持(Not Implemented),即域名服务器不支持查询类型;当值为 5 时,表示拒绝(Refused),一般是服务器由于设置的策略拒绝给出应答,如服务器不希望对某些请求者给出应答。
在请求中 Questions 的值不可能为 0;Answer RRs,Authority RRs,Additional RRs 的值都为 0,因为在请求中还没有响应的查询结果信息。这些信息在响应包中会有相应的值。
Answer RRs,Authority RRs,Additional RRs 都有了相应的值(不一定全为 0)
问题部分
问题部分指的是报文格式中查询问题区域(Queries)部分。该部分是用来显示 DNS 查询请求的问题,通常只有一个问题。该部分包含正在进行的查询信息,包含查询名(被查询主机名字)、查询类型、查询类。
查询名:一般为要查询的域名,有时也会是 IP 地址,用于反向查询。
查询类型:DNS 查询请求的资源类型。通常查询类型为 A 类型,表示由域名获取对应的 IP 地址。
查询类:地址类型,通常为互联网地址,值为 1。
https://www.jianshu.com/p/b483300378af
DNS资源记录:A记录/AAAA记录、CNAME、MX、NS、TXT、SPF
【domain】 IN A 【IP地址】
【domain】 IN AAAA 【IP地址】
【別名】 IN CNAME 【原名】
jd.com,jd360.com,jingdong.com虽然是不同名字的域名,但是可以指向同一个原名jd.com。可以让企业的对外展示更加灵活。
【domain】 IN MX 【优先度】 【邮件服务器】
邮件路由记录
【domain】 IN NS 【DNS服务器】
指定域名解析服务器
【domain】 IN TXT 【任意字符串】
一般指某个主机名或域名的说明,或者联系方式,或者标注提醒等等。
【domain】 IN TXT 【送信侧邮件服务器确认规则】
SPF记录是TXT记录的一个运用。后面的备注需要按照指定的格式才能有效。
基于TCP的域名解析
https://ephen.me/2017/dns-tcp/
DNS 是同时占用 UDP 和 TCP 的 53 端口传输数据的,这种单个应用协议同时使用两种传输协议的情况,在 TCP/IP 栈中也算是个异类。
当我们在使用 tcpdump 、 WireShark 等抓包工具分析时,会发现几乎所有的 DNS 应用都是在使用 UDP 传输。
DNS在什么情况下才会用到 TCP 呢?
由于UDP是按照数据包来传输的,整个包最大只能 512 字节。一般正常情况下,用来 DNS 查询是绰绰有余的。但当解析配置过于复杂,或者记录值过长(如: TXT 、 RRSIG 记录)使得 UDP 无法承载的情况下,就有必要转换 TCP 查询。
由于基于 TCP 的域名解析非常少见,大多数域名厂商是不支持的, CloudXNS 也不例外。然而同域名同主机同线路的同类型解析将会封装到一个 UDP 包传输,如果内容过多(超过 512 字节),该数据包可能会承载不下。
这也是为什么 CloudXNS 中即使 A 、 TXT 、 MX 等类型记录尽管不与自身互斥,我们仍然会对同域名下同一主机同一线路限制解析记录条数的原因。
指纹识别代码
来自fingerprintx
package dns
import (
"bytes"
"crypto/rand"
"net"
"time"
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
utils "github.com/praetorian-inc/fingerprintx/pkg/plugins/pluginutils"
)
const DNS = "dns"
type UDPPlugin struct{}
type TCPPlugin struct{}
func init() {
plugins.RegisterPlugin(&UDPPlugin{})
plugins.RegisterPlugin(&TCPPlugin{})
}
func CheckDNS(conn net.Conn, timeout time.Duration) (bool, error) {
for attempts := 0; attempts < 3; attempts++ {
transactionID := make([]byte, 2)
_, err := rand.Read(transactionID)
if err != nil {
return false, &utils.RandomizeError{Message: "Transaction ID"}
}
InitialConnectionPackage := append(transactionID, []byte{ //nolint:gocritic
// Transaction ID
0x01, 0x00, // Flags: 0x0100 Standard query
0x00, 0x01, // Questions: 1
0x00, 0x00, // Answer RRs: 0
0x00, 0x00, // Authority RRs: 0
0x00, 0x00, // Additional RRs: 0
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x04, 0x62, 0x69, 0x6e, 0x64, 0x00, // Name: version.bind
0x00, 0x10, // Type: TXT (Text strings) (16)
0x00, 0x03, // Class: CH (0x0003)
}...)
if conn.RemoteAddr().Network() == "tcp" {
InitialConnectionPackage = append([]byte{0x00, 0x1e}, InitialConnectionPackage...)
}
response, err := utils.SendRecv(conn, InitialConnectionPackage, timeout)
if err != nil {
return false, err
}
if len(response) == 0 {
return false, nil
}
if conn.RemoteAddr().Network() == "udp" {
if !bytes.Equal(transactionID[0:1], response[0:1]) {
return false, nil
}
}
if conn.RemoteAddr().Network() == "tcp" {
if !bytes.Equal(transactionID[0:1], response[2:3]) {
return false, nil
}
}
}
return true, nil
}
func (p *UDPPlugin) Run(conn net.Conn, timeout time.Duration, target plugins.Target) (*plugins.Service, error) {
isDNS, err := CheckDNS(conn, timeout)
if err != nil {
return nil, err
}
if isDNS {
payload := plugins.ServiceDNS{}
return plugins.CreateServiceFrom(target, payload, false, "", plugins.UDP), nil
}
return nil, nil
}
func (p *UDPPlugin) PortPriority(i uint16) bool {
return i == 53
}
func (p UDPPlugin) Name() string {
return DNS
}
func (p *UDPPlugin) Type() plugins.Protocol {
return plugins.UDP
}
func (p TCPPlugin) Run(conn net.Conn, timeout time.Duration, target plugins.Target) (*plugins.Service, error) {
isDNS, err := CheckDNS(conn, timeout)
if err != nil {
return nil, err
}
if isDNS {
payload := plugins.ServiceDNS{}
return plugins.CreateServiceFrom(target, payload, false, "", plugins.TCP), nil
}
return nil, nil
}
func (p TCPPlugin) PortPriority(i uint16) bool {
return i == 53
}
func (p TCPPlugin) Name() string {
return DNS
}
func (p *TCPPlugin) Priority() int {
return 50
}
func (p *UDPPlugin) Priority() int {
return 50
}
func (p TCPPlugin) Type() plugins.Protocol {
return plugins.TCP
}
构造的请求包类似
向某个DNS服务器发送下面的请求即可获得版本信息
dig @115.124.17.156 version.bind chaos txt