python实现dns劫持_DNS域传送漏洞(四) — python实现DNS axfr客户端[Query]

本文介绍了如何使用Python编写一个简单的DNS客户端,专注于实现AXFR查询以获取域名的全部记录,特别是A记录。通过理解DNS消息的结构,包括Header和Question区块,以及设置必要的字段,如ID、QR、QDCOUNT等,来构造查询请求。
摘要由CSDN通过智能技术生成

在DNS查询中,axfr类型是Authoritative Transfer的缩写,指请求传送某个区域的全部记录。

前面三篇日志中,笔者分别使用nmap、dig、nslookup来查询域传送记录。

本篇介绍自己动手,用python写一个简单的DNS客户端,仅实现axfr查询,并且只处理A记录。

DNS消息的格式

DNS请求和响应,都是由5个区块组成的,如下图所示:

+---------------------+

| Header |

+---------------------+

| Question | the question for the name server

+---------------------+

| Answer | RRs answering the question

+---------------------+

| Authority | RRs pointing toward an authority

+---------------------+

| Additional | RRs holding additional information

+---------------------+

1

2

3

4

5

6

7

8

9

10

11

+---------------------+

|Header|

+---------------------+

|Question|thequestionforthenameserver

+---------------------+

|Answer|RRsansweringthequestion

+---------------------+

|Authority|RRspointingtowardanauthority

+---------------------+

|Additional|RRsholdingadditionalinformation

+---------------------+

axfr请求的包,只填充header和Question区块就可以了。

Header区块的格式

Header的格式是这样的:

1 1 1 1 1 1

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| ID |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|QR| Opcode |AA|TC|RD|RA| Z | RCODE |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| QDCOUNT |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| ANCOUNT |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| NSCOUNT |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| ARCOUNT |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

111111

0123456789012345

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|ID|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|QR|Opcode|AA|TC|RD|RA|Z|RCODE|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|QDCOUNT|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|ANCOUNT|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|NSCOUNT|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|ARCOUNT|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

可以看到,每一行是16位,两个字节。整个Header的大小是12字节:

ID是随机值,2个字节,介于1到65535之间。客户端发送一个随机值,DNS服务器会原样返回该ID。

第二行是几个标记的集合:QR标记该消息是Query还是Response,查询的时候填充0,响应的时候填充1。而RCODE是Response Code(响应码),只有该值为0的时候才表示查询成功了。得到服务器响应之后应该检查这个值。

QDCOUNT填充Question的个数、ANCOUNT填充Answer的个数,NSCOUNT填充DNS权威服务器的个数,ARCOUNT填充附加记录的个数。对于本文要实现的axfr查询,分别填充: 1, 0, 0, 0。

下图是笔者用WireShak抓包得到一个axfr请求的Header:

图中Additional RRs的值为1,在尾部附加了一些额外的参数。编码的简化,可以不提供。

Question区块的格式

Question区块的基本格式是:

1 1 1 1 1 1

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| |

/ QNAME /

/ /

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| QTYPE |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

| QCLASS |

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

1

2

3

4

5

6

7

8

9

10

11

111111

0123456789012345

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

||

/QNAME/

//

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|QTYPE|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

|QCLASS|

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

QNAME是需要查询的域名,比如nwpu.edu.cn。它由一个Lable序列来表示。

格式是这样的:

用点号“.”把域名分割为几个Label,每个label前面带上字符串长度,nwpu.edu.cn.cn最终变为:

4nwpu3edu2cn

结尾处跟一个字节的空白。笔者抓包截图如下:

04表示有4个字符,而6e 77 70 75正好是nwpu。

QTYPE是一个类型常量,axfr记录的值是252。

QCLASS设置为1,表示Inernet。

Python组合一个Query消息

介绍完上面的内容,我们已经可以组一个查询消息了。函数如下:

def gen_query(domain):

import random

TRANS_ID = random.randint(1, 65535) # random ID

FLAGS = 0; QDCOUNT = 1; ANCOUNT = 0; NSCOUNT = 0; ARCOUNT = 0

data = struct.pack(

'!HHHHHH',

TRANS_ID, FLAGS,QDCOUNT, ANCOUNT, NSCOUNT, ARCOUNT

)

query = ''

for label in domain.strip().split('.'):

query += struct.pack('!B', len(label)) + label.lower()

query += '\x00' # end of domain name

data += query

global LEN_QUERY

LEN_QUERY = len(query) # length of query section

q_type = 252 # Type AXFR = 252

q_class = 1 # CLASS IN

data += struct.pack('!HH', q_type, q_class)

data = struct.pack('!H', len(data) ) + data # first 2 bytes should be length

return data

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

defgen_query(domain):

importrandom

TRANS_ID=random.randint(1,65535)# random ID

FLAGS=0;QDCOUNT=1;ANCOUNT=0;NSCOUNT=0;ARCOUNT=0

data=struct.pack(

'!HHHHHH',

TRANS_ID,FLAGS,QDCOUNT,ANCOUNT,NSCOUNT,ARCOUNT

)

query=''

forlabelindomain.strip().split('.'):

query+=struct.pack('!B',len(label))+label.lower()

query+='\x00'# end of domain name

data+=query

globalLEN_QUERY

LEN_QUERY=len(query)# length of query section

q_type=252# Type AXFR = 252

q_class=1# CLASS IN

data+=struct.pack('!HH',q_type,q_class)

data=struct.pack('!H',len(data))+data# first 2 bytes should be length

returndata

struct.pack用来格式化字符串,第一个参数中的惊叹号“!”表示字节序使用network (= big-endian)。

B代表Byte,一个字节。H代表unsigned short,两个字节。

消息开头的前两个字节,需要提供消息正文的长度。

因为在decode response时,还要用到Query的长度,所以我将它保存到了一个全局的LEN_QUERY变量中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值