anubis-netsec 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
anubis/API.py ADDED
@@ -0,0 +1,5 @@
1
+ # Public Shodan key bundled with Anubis
2
+ CENSYS_ID = ""
3
+ CENSYS_SECRET = ""
4
+ # Spyse Token - https://spyse.com/user
5
+ SPYSE_TOKEN = ""
anubis/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = '1.4.0'
anubis/cli.py ADDED
@@ -0,0 +1,132 @@
1
+ """
2
+ Usage:
3
+ anubis (-t TARGET | -f FILE) [-o FILENAME] [-bdinoprsSv] [-w SCAN] [-q NUM]
4
+ anubis -h
5
+ anubis (--version | -V)
6
+
7
+ Options:
8
+ -h --help show this help message and exit
9
+ -t --target set target (comma separated, no spaces, if multiple)
10
+ -f --file set target (reads from file, one domain per line)
11
+ -n --with-nmap perform an nmap service/script scan
12
+ -o --output save to filename
13
+ -i --additional-info show additional information about the host from Shodan (requires API key)
14
+ -p --ip outputs the resolved IPs for each subdomain, and a full list of unique ips
15
+ -d --send-to-anubis-db send results to Anubis-DB
16
+ -r --recursive recursively search over all subdomains
17
+ -s --ssl run an ssl scan and output cipher + chain info
18
+ -S --silent only out put subdomains, one per line
19
+ -w --overwrite-nmap-scan SCAN overwrite default nmap scan (default -nPn -sV -sC)
20
+ -v --verbose print debug info and full request output
21
+ -q --queue-workers NUM override number of queue workers (default: 10, max: 100)
22
+ -V --version show version and exit
23
+
24
+ Help:
25
+ For help using this tool, please open an issue on the Github repository:
26
+ https://github.com/jonluca/anubis
27
+ """
28
+
29
+ import sys
30
+ import time
31
+ from functools import reduce
32
+
33
+ from docopt import docopt
34
+
35
+ import anubis.commands
36
+ # noinspection PyPep8Naming
37
+ from . import __version__ as VERSION
38
+
39
+
40
+ # Overload stdout to save output and change colors on filewrite
41
+ class StdOutHook:
42
+ lines = []
43
+ filename = ""
44
+
45
+ def __init__(self, filename, silent, output):
46
+ self.filename = filename
47
+ self.silent = silent
48
+ self.output = output
49
+
50
+ def write(self, text, override=False, **kwargs):
51
+ if not self.silent or override:
52
+ sys.__stdout__.write(text)
53
+ self.lines.append(text)
54
+
55
+ def write_out(self):
56
+ if self.output:
57
+ with open(self.filename, "w") as file:
58
+ for line in self.lines:
59
+ # remove stdout colors
60
+ line = line.replace('\033[91m', '')
61
+ line = line.replace('\033[92m', '')
62
+ line = line.replace('\033[93m', '')
63
+ line = line.replace('\033[94m', '')
64
+ line = line.replace('\033[95m', '')
65
+ line = line.replace('\033[0m', '')
66
+ file.write(line)
67
+
68
+ def flush(self):
69
+ # python3 compatability, does nothing
70
+ pass
71
+
72
+
73
+ # credit to https://stackoverflow.com/questions/1557571/how-do-i-get-time-of-a-python-programs-execution
74
+ def seconds_to_str(t):
75
+ return "%d:%02d:%02d.%03d" % reduce(lambda ll, b: divmod(ll[0], b) + ll[1:],
76
+ [(t * 1000,), 1000, 60, 60])
77
+
78
+
79
+ def main():
80
+ try:
81
+ if sys.version_info < (3, 0):
82
+ sys.stdout.write("Sorry, requires Python 3.x, not Python 2.x\n")
83
+ sys.exit(1)
84
+
85
+ start_time = time.time()
86
+
87
+ options = docopt(__doc__, version=VERSION)
88
+
89
+ if options["--output"] or options['--silent']:
90
+ sys.stdout = StdOutHook(options["FILENAME"], options['--silent'],
91
+ options['--output'])
92
+
93
+ if options["--queue-workers"]:
94
+ if not options["--recursive"]:
95
+ print("Queue workers override only works with recursive option!")
96
+ sys.exit(1)
97
+ if int(options["--queue-workers"]) > 100:
98
+ print("Max queue worker override is 100!")
99
+ sys.exit(1)
100
+ if int(options["--queue-workers"]) < 1:
101
+ print("Queue workers can't be negative!")
102
+ sys.exit(1)
103
+
104
+ if not options["--target"] and not options['--file']:
105
+ print(
106
+ "Target required! Run with -h for usage instructions. Either -t target.host or -f file.txt required")
107
+ return
108
+
109
+ if options["--target"] and options['--file']:
110
+ print(
111
+ "Please only supply one target method - either read by file with -f or as an argument to -t, not both.")
112
+ return
113
+
114
+ print("""
115
+ d8888 888 d8b
116
+ d88888 888 Y8P
117
+ d88P888 888
118
+ d88P 888 88888b. 888 888 88888b. 888 .d8888b
119
+ d88P 888 888 "88b 888 888 888 "88b 888 88K
120
+ d88P 888 888 888 888 888 888 888 888 "Y8888b.
121
+ d8888888888 888 888 Y88b 888 888 d88P 888 X88
122
+ d88P 888 888 888 "Y88888 88888P" 888 88888P'
123
+ """)
124
+
125
+ command = anubis.commands.Target(options)
126
+ command.run()
127
+ print("Subdomain search took %s" % seconds_to_str(time.time() - start_time))
128
+ if options["--output"]:
129
+ sys.stdout.write_out()
130
+ except KeyboardInterrupt:
131
+ print("\nQuitting...")
132
+ sys.exit(0)
@@ -0,0 +1 @@
1
+ from .target import *
@@ -0,0 +1,14 @@
1
+ """The base command."""
2
+
3
+
4
+ class Base(object):
5
+ """A base command."""
6
+
7
+ def __init__(self, options, *args, **kwargs):
8
+ self.options = options
9
+ self.args = args
10
+ self.kwargs = kwargs
11
+
12
+ def run(self):
13
+ raise NotImplementedError(
14
+ 'run() method must be implemented by the overloading class')
@@ -0,0 +1,174 @@
1
+ """The target command."""
2
+ import os
3
+ import re
4
+ import socket
5
+ import sys
6
+ import threading
7
+ from urllib.parse import urlsplit
8
+
9
+ from anubis.scanners.anubis_db import search_anubisdb, send_to_anubisdb
10
+ from anubis.scanners.crt import search_crtsh
11
+ from anubis.scanners.hudson_rock import search_hudsonrock
12
+ from anubis.scanners.dnssec import dnssecc_subdomain_enum
13
+ from anubis.scanners.hackertarget import subdomain_hackertarget
14
+ from anubis.scanners.netcraft import search_netcraft
15
+ from anubis.scanners.nmap import scan_host
16
+ from anubis.scanners.recursive import recursive_search
17
+ from anubis.scanners.shodan import search_shodan
18
+ from anubis.scanners.spyse import search_spyse
19
+ from anubis.scanners.ssl import search_subject_alt_name
20
+ from anubis.scanners.zonetransfer import dns_zonetransfer
21
+ from anubis.utils.color_print import ColorPrint
22
+ from .base import Base
23
+
24
+
25
+ class Target(Base):
26
+ """Main enumeration module"""
27
+ domains = list()
28
+ dedupe = set()
29
+
30
+ stdout = sys.stdout
31
+
32
+ def handle_exception(self, e, message=""):
33
+ if self.options["--verbose"]:
34
+ print(e)
35
+ if message:
36
+ ColorPrint.red(message)
37
+
38
+ def init(self):
39
+ if self.options["FILE"]:
40
+ full_path = os.path.join(os.getcwd(), self.options["FILE"])
41
+ with open(full_path) as file:
42
+ self.options["TARGET"] = list(filter(None, file.read().split('\n')))
43
+ else:
44
+ self.options["TARGET"] = list(
45
+ filter(None, self.options["TARGET"].split(",")))
46
+ # Clean up targets
47
+ for i in range(len(self.options["TARGET"])):
48
+ url = self.options["TARGET"][i]
49
+ # Inject protocol if not there
50
+ if not re.match(r'http(s?):', url):
51
+ url = 'http://' + url
52
+
53
+ parsed = urlsplit(url)
54
+ host = parsed.netloc
55
+
56
+ self.options["TARGET"][i] = host
57
+
58
+ try:
59
+ ip_str = socket.gethostbyname(host)
60
+ ColorPrint.green(f"Searching for subdomains for {ip_str} ({host})")
61
+ except Exception as e:
62
+ self.handle_exception(e,
63
+ "Error connecting to target! Make sure you spelled it correctly and it is a resolvable address")
64
+
65
+ def run(self):
66
+ # Retrieve IP of target and run initial configurations
67
+ self.init()
68
+ # If multiple targets, create scans for each
69
+ for i in range(len(self.options["TARGET"])):
70
+ # Default scans that run every time
71
+ target = self.options["TARGET"][i]
72
+ ColorPrint.green(f"Working on target: {target}")
73
+ threads = [threading.Thread(target=dns_zonetransfer, args=(self, target)),
74
+ threading.Thread(target=subdomain_hackertarget,
75
+ args=(self, target)),
76
+ threading.Thread(target=search_subject_alt_name,
77
+ args=(self, target)),
78
+ threading.Thread(target=search_netcraft, args=(self, target)),
79
+ threading.Thread(target=search_hudsonrock, args=(self, target)),
80
+ threading.Thread(target=search_crtsh, args=(self, target)),
81
+ threading.Thread(target=search_spyse,
82
+ args=(self, target)),
83
+ threading.Thread(target=search_anubisdb, args=(self, target))]
84
+
85
+ # Additional options - shodan.io scan
86
+ if self.options["--additional-info"]:
87
+ threads.append(threading.Thread(target=search_shodan, args=(self,)))
88
+
89
+ # Additional options - nmap scan of dnssec script and a host/port scan
90
+ if self.options["--with-nmap"]:
91
+ threads.append(
92
+ threading.Thread(target=dnssecc_subdomain_enum, args=(self, target)))
93
+ threads.append(threading.Thread(target=scan_host, args=(self, target)))
94
+
95
+ # Start all threads and wait for them to finish
96
+ for x in threads:
97
+ x.start()
98
+
99
+ for x in threads:
100
+ x.join()
101
+
102
+ # Run a recursive search on each subdomain - rarely useful, but nice to have
103
+ # just in case
104
+ if self.options["--recursive"]:
105
+ recursive_search(self)
106
+
107
+ # remove duplicates and clean up
108
+ self.domains = self.clean_domains(self.domains)
109
+ self.dedupe = set(self.domains)
110
+
111
+ print("Found", len(self.dedupe), "subdomains")
112
+ print("----------------")
113
+
114
+ if self.options["--ip"]:
115
+ self.resolve_ips()
116
+ else:
117
+ for domain in self.dedupe:
118
+ cleaned_domain = domain.strip()
119
+ ColorPrint.green(cleaned_domain)
120
+ if self.options['--silent']:
121
+ sys.stdout.write(cleaned_domain + '\n', override=True)
122
+
123
+ if self.options["--send-to-anubis-db"]:
124
+ send_to_anubisdb(self, [target])
125
+ # reset per domain
126
+ self.domains = list()
127
+
128
+ def resolve_ips(self):
129
+ unique_ips = set()
130
+ for domain in self.dedupe:
131
+ try:
132
+ # Attempt to get IP
133
+ resolved_ip = socket.gethostbyname(domain)
134
+ except Exception as e:
135
+ # If getting IP fails, fallback to empty string
136
+ resolved_ip = ""
137
+ # TODO - Align domains and ips in stdout
138
+ ColorPrint.green(domain + ": " + resolved_ip)
139
+ if self.options['--silent']:
140
+ sys.stdout.write(domain + '\n', override=True)
141
+
142
+ if resolved_ip:
143
+ unique_ips.add(resolved_ip)
144
+ print("Found %s unique IPs" % len(unique_ips))
145
+ for ip in unique_ips:
146
+ # Ignore empty strings, final sanity check
147
+ if ip:
148
+ ColorPrint.green(ip)
149
+
150
+ @staticmethod
151
+ def clean_domains(domains):
152
+ cleaned = []
153
+ for subdomain in domains:
154
+ subdomain = subdomain.lower()
155
+ if subdomain.find("//") != -1:
156
+ subdomain = subdomain[subdomain.find("//") + 2:]
157
+ # Some pkey return instances like example.com. - remove the final .
158
+ if subdomain.endswith('.'):
159
+ subdomain = subdomain[:-1]
160
+ # sometimes we'll get something like /www.example.com
161
+ if subdomain[0] in ["\\", ".", "/", "#", "$", "%"]:
162
+ subdomain = subdomain[1:]
163
+ # If it's an email address, only take the domain part
164
+ if "@" in subdomain:
165
+ subdomain = subdomain.split("@")
166
+ # If it's an actual email like mail@example.com, take example.com
167
+ if len(subdomain) > 1:
168
+ subdomain = subdomain[1]
169
+ else:
170
+ # If for some reason it's example.com@, take example.com
171
+ subdomain = subdomain[0]
172
+
173
+ cleaned.append(subdomain.strip())
174
+ return cleaned
File without changes
@@ -0,0 +1,29 @@
1
+ from json import loads
2
+
3
+ import requests
4
+
5
+ from anubis.utils.color_print import ColorPrint
6
+
7
+
8
+ def search_anubisdb(self, target):
9
+ print("Searching Anubis-DB")
10
+ res = requests.get("https://anubisdb.com/subdomains/" + target)
11
+ if res.status_code == 200 and res.text:
12
+ subdomains = loads(res.text)
13
+ for subdomain in subdomains:
14
+ if subdomain not in self.domains:
15
+ self.domains.append(subdomain)
16
+
17
+
18
+ def send_to_anubisdb(self, target):
19
+ if len(target) == 1:
20
+ print("Sending to AnubisDB")
21
+ data = {'subdomains': self.domains}
22
+ # Sends found subdomains to Anubis (max 10,000/post)
23
+ res = requests.post("https://anubisdb.com/subdomains/" + target[0],
24
+ json=data)
25
+ if res.status_code != 200:
26
+ ColorPrint.red("Error sending results to AnubisDB - Status Code: " + str(
27
+ res.status_code))
28
+ else:
29
+ print("Cannot send multiple domains to AnubisDB")
@@ -0,0 +1,21 @@
1
+ import censys
2
+
3
+ from anubis.utils.color_print import ColorPrint
4
+
5
+
6
+ def search_censys(self, target):
7
+ print("Searching Censys")
8
+ try:
9
+ from anubis.API import CENSYS_ID, CENSYS_SECRET
10
+ except ImportError:
11
+ ColorPrint.red(
12
+ "To run a Censys scan, you must add your API keys to anubis/API.py")
13
+ return
14
+ if not CENSYS_SECRET or not CENSYS_ID:
15
+ ColorPrint.red(
16
+ "To run a Censys scan, you must add your API keys to anubis/API.py")
17
+ return
18
+ # Print certificate information for domains
19
+ c = censys.certificates.CensysCertificates(CENSYS_ID, CENSYS_SECRET)
20
+ for cert in c.search("." + target):
21
+ print(cert)
anubis/scanners/crt.py ADDED
@@ -0,0 +1,132 @@
1
+ import re
2
+ from html.parser import HTMLParser
3
+
4
+ import requests
5
+
6
+
7
+ def search_crtsh(self, target):
8
+ """
9
+ Search crt.sh for certificates related to the target and extract both
10
+ subdomains and root domains.
11
+ """
12
+ print("Searching crt.sh")
13
+ headers = {
14
+ 'authority': 'crt.sh',
15
+ 'cache-control': 'max-age=0',
16
+ 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.28 Safari/537.36',
17
+ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
18
+ 'accept-encoding': 'gzip, deflate, br',
19
+ 'accept-language': 'en-US,en;q=0.9',
20
+ }
21
+
22
+ # Clean target input to ensure we have just the domain
23
+ target = clean_domain(target)
24
+
25
+ # Search for both wildcard and explicit domain
26
+ params = (('q', '%.' + target),)
27
+ try:
28
+ res = requests.get('https://crt.sh/', headers=headers, params=params)
29
+ scraped = res.text
30
+
31
+ # Extract domains from response
32
+ domains = extract_domains(scraped, target)
33
+
34
+ # Add all found domains to our list
35
+ for domain in domains:
36
+ if domain not in self.domains:
37
+ self.domains.append(domain)
38
+ if self.options["--verbose"]:
39
+ print("Crt.sh Found Domain:", domain)
40
+
41
+ except Exception as e:
42
+ self.handle_exception(e, "Error searching crt.sh")
43
+
44
+
45
+ def clean_domain(domain):
46
+ """Clean input to get the base domain without protocol or path."""
47
+ # Remove protocol if present
48
+ domain = re.sub(r'^(http|https)://', '', domain)
49
+ # Remove path, query params, etc.
50
+ domain = domain.split('/')[0].strip()
51
+ return domain
52
+
53
+
54
+ class _CertificateNamesParser(HTMLParser):
55
+ """Collect direct cell text, separating names at BR and cell boundaries.
56
+
57
+ Unsupported nested markup discards the current name and the rest of its cell.
58
+ """
59
+
60
+ _CELL_BOUNDARIES = ('td', 'th', 'tr', 'thead', 'tbody', 'tfoot', 'table')
61
+
62
+ def __init__(self):
63
+ super().__init__(convert_charrefs=True)
64
+ self.names = []
65
+ self._parts = None
66
+
67
+ def _finish_name(self):
68
+ if self._parts is not None:
69
+ self.names.append(''.join(self._parts))
70
+ self._parts = None
71
+
72
+ def handle_starttag(self, tag, attrs):
73
+ if tag == 'td':
74
+ self._finish_name()
75
+ self._parts = []
76
+ elif tag == 'br' and self._parts is not None:
77
+ self._finish_name()
78
+ self._parts = []
79
+ elif tag in self._CELL_BOUNDARIES:
80
+ self._finish_name()
81
+ elif self._parts is not None:
82
+ # Do not interpret scripts, links, or other nested markup as names.
83
+ self._parts = None
84
+
85
+ def handle_endtag(self, tag):
86
+ if tag in self._CELL_BOUNDARIES:
87
+ self._finish_name()
88
+ elif tag == 'br' and self._parts is not None:
89
+ self._finish_name()
90
+ self._parts = []
91
+ elif self._parts is not None:
92
+ self._parts = None
93
+
94
+ def handle_data(self, data):
95
+ if self._parts is not None:
96
+ self._parts.append(data)
97
+
98
+ def close(self):
99
+ super().close()
100
+ self._finish_name()
101
+
102
+
103
+ def extract_domains(html_content, target):
104
+ """Return concrete in-scope hostnames, always including the normalized target.
105
+
106
+ Wildcard certificate names are skipped: stripping ``*.`` would invent a
107
+ concrete hostname. As before, labels preceding the caller's target must be
108
+ ASCII DNS labels. The target suffix is not revalidated or converted via IDNA.
109
+ """
110
+ target = target.strip().lower()
111
+ domains = {target}
112
+ parser = _CertificateNamesParser()
113
+ parser.feed(html_content)
114
+ parser.close()
115
+ label_pattern = re.compile(r'[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?')
116
+ target_labels = len(target.split('.'))
117
+
118
+ for name in parser.names:
119
+ domain = name.strip()
120
+ if '*' in domain or len(domain.rstrip('.')) > 253:
121
+ continue
122
+ if target.isascii() and not domain.isascii():
123
+ continue
124
+ prefix_labels = domain.split('.')[:-target_labels]
125
+ domain = domain.lower()
126
+ if domain != target and not domain.endswith('.' + target):
127
+ continue
128
+ if all(label.isascii() and label_pattern.fullmatch(label.lower())
129
+ for label in prefix_labels):
130
+ domains.add(domain)
131
+
132
+ return list(domains)
@@ -0,0 +1,23 @@
1
+ import os
2
+
3
+ from nmap import nmap
4
+
5
+ from anubis.utils.color_print import ColorPrint
6
+
7
+
8
+ def dnssecc_subdomain_enum(self, target):
9
+ # Must run as root
10
+ if os.getuid() == 0:
11
+ print("Starting DNSSEC Enum")
12
+ nm = nmap.PortScanner()
13
+ arguments = '-sSU -p 53 --script dns-nsec-enum --script-args dns-nsec-enum.domains=' + target
14
+
15
+ nm.scan(hosts=target, arguments=arguments)
16
+ for host in nm.all_hosts():
17
+ try:
18
+ print(nm[host]['udp'][53]['script']['dns-nsec-enum'])
19
+ except:
20
+ pass
21
+ else:
22
+ ColorPrint.red(
23
+ "To run a DNSSEC subdomain enumeration, Anubis must be run as root")
@@ -0,0 +1,24 @@
1
+ import requests
2
+
3
+
4
+ def subdomain_hackertarget(self, target):
5
+ print("Searching HackerTarget")
6
+ headers = {
7
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', }
8
+ params = (('q', target),)
9
+
10
+ results = requests.get('http://api.hackertarget.com/hostsearch/',
11
+ headers=headers, params=params)
12
+ results = results.text.split('\n')
13
+ for res in results:
14
+ try:
15
+ if res.split(",")[0] != "":
16
+ domain = res.split(",")[0]
17
+ domain = domain.strip()
18
+ if domain not in self.domains and domain.endswith("." + target):
19
+ self.domains.append(domain)
20
+ if self.options["--verbose"]:
21
+ print("HackerTarget Found Domain:", domain.strip())
22
+ except:
23
+ print("except")
24
+ pass
@@ -0,0 +1,24 @@
1
+ from urllib.parse import urlsplit
2
+
3
+ import requests
4
+
5
+
6
+ def search_hudsonrock(self, target):
7
+ print("Searching HudsonRock")
8
+ try:
9
+ res = requests.get("https://cavalier.hudsonrock.com/api/json/v2/osint-tools/search-by-domain?domain=" + target)
10
+ urls = res.json().get('data', {}).get('all_urls', [])
11
+ except Exception as e:
12
+ self.handle_exception(e, "Error searching HudsonRock")
13
+ return
14
+
15
+ for url_entry in urls:
16
+ try:
17
+ # Entries are full URLs (https://sso.example.com/login); keep the host
18
+ domain = urlsplit(url_entry.get('url', '')).hostname
19
+ except (AttributeError, ValueError):
20
+ continue
21
+ if domain and domain.endswith("." + target) and domain not in self.domains:
22
+ self.domains.append(domain)
23
+ if self.options["--verbose"]:
24
+ print("HudsonRock Found Domain:", domain)
@@ -0,0 +1,34 @@
1
+ import re
2
+
3
+ import requests
4
+
5
+
6
+ def search_netcraft(self, target):
7
+ print("Searching NetCraft.com")
8
+ headers = {'Pragma': 'no-cache', 'DNT': '1',
9
+ 'Accept-Encoding': 'gzip, deflate, br',
10
+ 'Accept-Language': 'en-US,en;q=0.9,it;q=0.8',
11
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36',
12
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
13
+ 'Cache-Control': 'no-cache',
14
+ 'Referer': 'https://searchdns.netcraft.com/?restriction=site+ends+with&host=',
15
+ 'Connection': 'keep-alive', }
16
+
17
+ params = (('restriction', 'site contains'), ('host', target))
18
+ try:
19
+ res = requests.get('https://searchdns.netcraft.com/', headers=headers,
20
+ params=params)
21
+ scraped = res.text
22
+ trimmed = scraped[scraped.find('<div class="blogtitle">'):scraped.rfind(
23
+ '<div id="copyright">')]
24
+ subdomain_finder = re.compile(
25
+ r'<a href="http://toolbar.netcraft.com/site_report\?url=(.*)">')
26
+ links = subdomain_finder.findall(trimmed)
27
+ for domain in links:
28
+ if domain.strip() not in self.domains and domain.endswith("." + target):
29
+ self.domains.append(domain.strip())
30
+ if self.options["--verbose"]:
31
+ print("Netcraft Found Domain:", domain.strip())
32
+ except Exception as e:
33
+ self.handle_exception(e, "Error searching NetCraft")
34
+ pass
@@ -0,0 +1,53 @@
1
+ import shutil
2
+ from json import dumps
3
+
4
+ import nmap
5
+
6
+
7
+ # Performs an nmap scan of a target, and outputs interesting services/ssl information
8
+ def scan_host(self, target):
9
+ if shutil.which("nmap") is None:
10
+ print("Nmap must be installed to run --with-nmap!")
11
+ return
12
+
13
+ print("Starting Nmap Scan")
14
+ nm = nmap.PortScanner()
15
+ arguments = self.options["--overwrite-nmap-scan"] or '-nPn -sV -sC'
16
+ nm.scan(hosts=target, arguments=arguments)
17
+ # Could be extended to scan multiple hosts in the future
18
+ for host in nm.all_hosts():
19
+ if self.options["--verbose"]:
20
+ print(dumps(nm[host], indent=2, sort_keys=True))
21
+ print('Host : %s (%s)' % (host, nm[host].hostname()))
22
+ for proto in nm[host].all_protocols():
23
+ print('----------')
24
+ print('Protocol: %s' % proto)
25
+ lport = nm[host][proto].keys()
26
+ for port in lport:
27
+ print('port: %s\tstate: %s' % (port, nm[host][proto][port]['state']))
28
+ try:
29
+ if nm[host][proto][port]['product']:
30
+ print('\tservice: %s' % nm[host][proto][port]['product'], end='')
31
+ if nm[host][proto][port]['version']:
32
+ print('\tversion: %s' % nm[host][proto][port]['version'])
33
+ else:
34
+ print('')
35
+ except Exception as e:
36
+ self.handle_exception(e)
37
+ try:
38
+ fix_newline = nm[host][proto][port]['script']['ssl-cert'].split('\n')
39
+ print('\tCertificate:')
40
+ for i in range(len(fix_newline)):
41
+ print('\t', fix_newline[i])
42
+ if fix_newline[i].startswith("Subject Alternative Name: "):
43
+ content = fix_newline[i].replace("Subject Alternative Name: ", '')
44
+ content = content.replace("DNS:", '')
45
+ new_domains = content.split(",")
46
+ for domain in new_domains:
47
+ domain = domain.strip()
48
+ if domain not in self.domains:
49
+ self.domains.append(domain)
50
+ if self.options["--verbose"]:
51
+ print("Nmap Found Domain:", domain.strip())
52
+ except Exception as e:
53
+ self.handle_exception(e)