-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdnscan
executable file
·179 lines (143 loc) · 6.16 KB
/
dnscan
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
#!/usr/bin/env python3
import argparse
import dns.resolver
import dns.query
import dns.zone
from concurrent.futures import wait, ThreadPoolExecutor
import logging
class Color:
BLUE = '\033[94m'
RED = '\033[91m'
YELLOW = '\033[93m'
ENDC = '\033[0m'
LOG_FORMAT = f'{Color.YELLOW}[%(asctime)s]{Color.ENDC} {Color.BLUE}[%(levelname)s]{Color.ENDC} - %(message)s'
logging.basicConfig(level=logging.INFO,
format=LOG_FORMAT)
logger = logging.getLogger(__name__)
record_types = ['A', 'AAAA', 'CNAME', 'MX', 'PTR', 'SOA', 'HINFO', 'TXT']
def gen_banner():
print("""
+==================================================+
| ########## # # |
| # ##### # ####### |
| ## # ######### # |
| ## ####### ## _# ## ####### |
| ## ## # ## ## |
| ## ## # ## ## |
+==================================================+
| スキャナー| Made By: Arthur Ottoni | スキャナー |
| Github: https://github.com/arthurhydr/DnScan |
+==================================================+
""")
def read_wordlist(filename):
wordlist = []
try:
with open(filename, 'r') as file:
for word in file:
wordlist.append(word.strip())
logger.info('Wordlist opened successfully.')
except FileNotFoundError:
logger.error(f'Wordlist file \'{filename}\' not found.')
return wordlist
def transfer_zone(host, nameserver):
try:
ns_name = dns.name.from_text(nameserver)
ns = dns.resolver.Resolver().resolve(ns_name, rdtype='A').response.answer[0][0].to_text()
z = dns.zone.from_xfr(dns.query.xfr(ns, host))
for name, node in z.nodes.items():
logger.info(f'{name} {node.to_text(name)}')
logger.info(f'{Color.BLUE}Successful zone-transfer to {nameserver}{Color.ENDC}')
except dns.zone.NoSOA:
logger.error(f'{Color.RED}Zone-transfer failed for {nameserver}{Color.ENDC}')
except Exception:
logger.error(f'{Color.RED}Error in zone-transfer to {nameserver}{Color.ENDC}')
def test_all_nameservers(host):
try:
resolver = dns.resolver.Resolver()
nameservers = resolver.resolve(host, 'NS')
return [str(nameserver.target) for nameserver in nameservers]
except dns.resolver.NXDOMAIN:
logger.error(f'{Color.RED}{host} NOT FOUND.{Color.ENDC}')
except Exception as e:
logger.error(f'{Color.RED}Testing Error: {str(e)}{Color.ENDC}')
def sub_domain_scan(host, wordlist, threads):
with ThreadPoolExecutor(threads) as executor:
scan = [executor.submit(sub_domain_scan_worker, host, word) for word in wordlist]
wait(scan)
def sub_domain_scan_worker(host, word):
try:
ip_value = dns.resolver.resolve(f'{word}.{host}', 'A')
if ip_value:
logger.info(f'{word}.{host}')
except dns.resolver.NXDOMAIN:
pass
except dns.resolver.NoAnswer:
pass
def possible_takeover(host, wordlist, threads):
with ThreadPoolExecutor(threads) as executor:
scan = [executor.submit(possible_takeover_worker, host, word) for word in wordlist]
wait(scan)
def possible_takeover_worker(host, word):
try:
answer = dns.resolver.resolve(f'{word}.{host}', 'CNAME')
for record in answer:
logger.info(f'{word}.{host} -> {record.to_text()}')
except dns.resolver.NXDOMAIN:
pass
except dns.resolver.NoAnswer:
pass
def dns_recon(host, wordlist, threads, flags):
with ThreadPoolExecutor(threads) as executor:
scan = [executor.submit(dns_recon_worker, host, word, flags) for word in wordlist]
wait(scan)
def dns_recon_worker(host, word, flags):
if(flags[0] == 'ALL'):
for flag in record_types:
try:
answer = dns.resolver.resolve(f'{word}.{host}', flag)
for data in answer:
logger.info(f'{word}.{host} {flag} -> {data.to_text()}')
except dns.resolver.NoAnswer:
pass
except dns.resolver.NXDOMAIN:
pass
else:
for flag in flags:
try:
answer = dns.resolver.resolve(f'{word}.{host}', flag)
for data in answer:
logger.info(f'{word}.{host} {flag} -> {data.to_text()}')
except dns.resolver.NoAnswer:
pass
except dns.resolver.NXDOMAIN:
pass
def main():
parser = argparse.ArgumentParser(description='DNS Security Testing Tool')
parser.add_argument('host', help='Host to perform DNS tests on')
parser.add_argument('wordlist', help='File containing a list of subdomains or keywords')
parser.add_argument('--threads', type=int, default=50, help='Number of threads to use for scanning (default: 50)')
parser.add_argument('--scan', choices=['subdomain', 'takeover', 'recon', 'all'], default='all', help='Type of scan to perform (default: all)')
parser.add_argument('--flags', nargs='+', default=['ALL'], help='DNS record types to use in DNS recon (default: ALL)')
args = parser.parse_args()
gen_banner()
wordlist = read_wordlist(args.wordlist)
try:
print('\n------------- Zone-Transfer -------------')
nameservers = test_all_nameservers(args.host)
for ns_server in nameservers:
logger.info(f'Testing nameserver: {ns_server}')
if args.scan == 'all':
transfer_zone(args.host, ns_server)
if args.scan == 'subdomain' or args.scan == 'all':
print('\n------------- Subdomain ---------------')
sub_domain_scan(args.host, wordlist, args.threads)
if args.scan == 'takeover' or args.scan == 'all':
print('\n------------- Possible Takeover -------------')
possible_takeover(args.host, wordlist, args.threads)
if args.scan == 'recon' or args.scan == 'all':
print('\n------------- DNS Recon -------------')
dns_recon(args.host, wordlist, args.threads, args.flags)
except KeyboardInterrupt:
quit()
if __name__ == '__main__':
main()