diff --git a/autorecon/config.py b/autorecon/config.py index d4004e9..c8162ce 100644 --- a/autorecon/config.py +++ b/autorecon/config.py @@ -42,7 +42,7 @@ configurable_boolean_keys = [ ] config = { - 'protected_classes': ['autorecon', 'target', 'service', 'commandstreamreader', 'plugin', 'portscan', 'servicescan', 'global', 'pattern'], + 'protected_classes': ['autorecon', 'target', 'service', 'commandstreamreader', 'plugin', 'portscan', 'report', 'servicescan', 'global', 'pattern'], 'service_exceptions': ['mc-nmf', 'ncacn_http', 'smux', 'status', 'tcpwrapped', 'unknown'], 'config_dir': config_dir, 'global_file': None, diff --git a/autorecon/default-plugins/dirbuster.py b/autorecon/default-plugins/dirbuster.py index 99cd998..4180f19 100644 --- a/autorecon/default-plugins/dirbuster.py +++ b/autorecon/default-plugins/dirbuster.py @@ -18,63 +18,70 @@ class DirBuster(ServiceScan): self.add_option('threads', default=10, help='The number of threads to use when directory busting. Default: %(default)s') self.add_option('ext', default='txt,html,php,asp,aspx,jsp', help='The extensions you wish to fuzz (no dot, comma separated). Default: %(default)s') self.add_true_option('recursive', help='Enables recursive searching (where available). Warning: This may cause significant increases to scan times. Default: %(default)s') + self.add_option('extras', default='', help='Any extra options you wish to pass to the tool when it runs. e.g. --dirbuster.extras=\'-s 200,301 --discover-backup\'') self.match_service_name('^http') self.match_service_name('^nacn_http$', negative_match=True) def check(self): tool = self.get_option('tool') - if tool == 'feroxbuster': - if which('feroxbuster') is None: - self.error('The feroxbuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install feroxbuster)') - elif tool == 'gobuster': - if which('gobuster') is None: - self.error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)') - elif tool == 'dirsearch': - if which('dirsearch') is None: - self.error('The dirsearch program could not be found. Make sure it is installed. (On Kali, run: sudo apt install dirsearch)') + if tool == 'feroxbuster' and which('feroxbuster') is None: + self.error('The feroxbuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install feroxbuster)') + return False + elif tool == 'gobuster' and which('gobuster') is None: + self.error('The gobuster program could not be found. Make sure it is installed. (On Kali, run: sudo apt install gobuster)') + return False + elif tool == 'dirsearch' and which('dirsearch') is None: + self.error('The dirsearch program could not be found. Make sure it is installed. (On Kali, run: sudo apt install dirsearch)') + return False + elif tool == 'ffuf' and which('ffuf') is None: + self.error('The ffuf program could not be found. Make sure it is installed. (On Kali, run: sudo apt install ffuf)') + return False + elif tool == 'dirb' and which('dirb') is None: + self.error('The dirb program could not be found. Make sure it is installed. (On Kali, run: sudo apt install dirb)') + return False async def run(self, service): dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')]) for wordlist in self.get_option('wordlist'): name = os.path.splitext(os.path.basename(wordlist))[0] if self.get_option('tool') == 'feroxbuster': - await service.execute('feroxbuster -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -x "' + self.get_option('ext') + '" -v -k ' + ('' if self.get_option('recursive') else '-n ') + '-q -e -o "{scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_' + name + '.txt"') + await service.execute('feroxbuster -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -x "' + self.get_option('ext') + '" -v -k ' + ('' if self.get_option('recursive') else '-n ') + '-q -e -o "{scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_' + name + '.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '')) elif self.get_option('tool') == 'gobuster': - await service.execute('gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e -k -x "' + self.get_option('ext') + '" -z -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_' + name + '.txt"') + await service.execute('gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e -k -x "' + self.get_option('ext') + '" -z -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_' + name + '.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '')) elif self.get_option('tool') == 'dirsearch': if service.target.ipversion == 'IPv6': service.error('dirsearch does not support IPv6.') else: - await service.execute('dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f -q ' + ('-r ' if self.get_option('recursive') else '') + '-w ' + wordlist + ' --format=plain -o "{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_' + name + '.txt"') + await service.execute('dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f -q ' + ('-r ' if self.get_option('recursive') else '') + '-w ' + wordlist + ' --format=plain -o "{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_' + name + '.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '')) elif self.get_option('tool') == 'ffuf': - await service.execute('ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e "' + dot_extensions + '" -v ' + ('-recursion ' if self.get_option('recursive') else '') + '-noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_' + name + '.txt') + await service.execute('ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w ' + wordlist + ' -e "' + dot_extensions + '" -v ' + ('-recursion ' if self.get_option('recursive') else '') + '-noninteractive' + (' ' + self.get_option('extras') if self.get_option('extras') else '') + ' | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_' + name + '.txt') elif self.get_option('tool') == 'dirb': - await service.execute('dirb {http_scheme}://{addressv6}:{port}/ ' + wordlist + ' -l ' + ('' if self.get_option('recursive') else '-r ') + '-S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_' + name + '.txt"') + await service.execute('dirb {http_scheme}://{addressv6}:{port}/ ' + wordlist + ' -l ' + ('' if self.get_option('recursive') else '-r ') + '-S -X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_' + name + '.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '')) def manual(self, service, plugin_was_run): dot_extensions = ','.join(['.' + x for x in self.get_option('ext').split(',')]) if self.get_option('tool') == 'feroxbuster': service.add_manual_command('(feroxbuster) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ - 'feroxbuster -u {http_scheme}://{addressv6}:{port} -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x "' + self.get_option('ext') + '" -v -k ' + ('' if self.get_option('recursive') else '-n ') + '-e -o {scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_dirbuster.txt' + 'feroxbuster -u {http_scheme}://{addressv6}:{port} -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x "' + self.get_option('ext') + '" -v -k ' + ('' if self.get_option('recursive') else '-n ') + '-e -o {scandir}/{protocol}_{port}_{http_scheme}_feroxbuster_dirbuster.txt' + (' ' + self.get_option('extras') if self.get_option('extras') else '') ]) elif self.get_option('tool') == 'gobuster': service.add_manual_command('(gobuster v3) Multi-threaded directory/file enumeration for web servers using various wordlists:', [ - 'gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -x "' + self.get_option('ext') + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_dirbuster.txt"' + 'gobuster dir -u {http_scheme}://{addressv6}:{port}/ -t ' + str(self.get_option('threads')) + ' -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e -k -x "' + self.get_option('ext') + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_gobuster_dirbuster.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '') ]) elif self.get_option('tool') == 'dirsearch': if service.target.ipversion == 'IPv4': service.add_manual_command('(dirsearch) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ - 'dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f ' + ('-r ' if self.get_option('recursive') else '') + '-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --format=plain --output="{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_dirbuster.txt"' + 'dirsearch -u {http_scheme}://{address}:{port}/ -t ' + str(self.get_option('threads')) + ' -e "' + self.get_option('ext') + '" -f ' + ('-r ' if self.get_option('recursive') else '') + '-w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --format=plain --output="{scandir}/{protocol}_{port}_{http_scheme}_dirsearch_dirbuster.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '') ]) elif self.get_option('tool') == 'ffuf': service.add_manual_command('(ffuf) Multi-threaded recursive directory/file enumeration for web servers using various wordlists:', [ - 'ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e "' + dot_extensions + '" -v ' + ('-recursion ' if self.get_option('recursive') else '') + '-noninteractive | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_dirbuster.txt' + 'ffuf -u {http_scheme}://{addressv6}:{port}/FUZZ -t ' + str(self.get_option('threads')) + ' -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -e "' + dot_extensions + '" -v ' + ('-recursion ' if self.get_option('recursive') else '') + '-noninteractive' + (' ' + self.get_option('extras') if self.get_option('extras') else '') + ' | tee {scandir}/{protocol}_{port}_{http_scheme}_ffuf_dirbuster.txt' ]) elif self.get_option('tool') == 'dirb': service.add_manual_command('(dirb) Recursive directory/file enumeration for web servers using various wordlists:', [ - 'dirb {http_scheme}://{addressv6}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l ' + ('' if self.get_option('recursive') else '-r ') + '-X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_dirbuster.txt"' + 'dirb {http_scheme}://{addressv6}:{port}/ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -l ' + ('' if self.get_option('recursive') else '-r ') + '-X ",' + dot_extensions + '" -o "{scandir}/{protocol}_{port}_{http_scheme}_dirb_dirbuster.txt"' + (' ' + self.get_option('extras') if self.get_option('extras') else '') ]) diff --git a/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py b/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py index b9306fb..8f082bb 100644 --- a/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py +++ b/autorecon/default-plugins/dnsrecon-subdomain-bruteforce.py @@ -16,6 +16,7 @@ class DnsReconSubdomainBruteforce(ServiceScan): def check(self): if which('dnsrecon') is None: self.error('The program dnsrecon could not be found. Make sure it is installed. (On Kali, run: sudo apt install dnsrecon)') + return False def manual(self, service, plugin_was_run): domain_name = '' diff --git a/autorecon/default-plugins/dnsrecon.py b/autorecon/default-plugins/dnsrecon.py index f53c51d..cfb9edc 100644 --- a/autorecon/default-plugins/dnsrecon.py +++ b/autorecon/default-plugins/dnsrecon.py @@ -16,6 +16,7 @@ class DnsRecon(ServiceScan): def check(self): if which('dnsrecon') is None: self.error('The program dnsrecon could not be found. Make sure it is installed. (On Kali, run: sudo apt install dnsrecon)') + return False def manual(self, service, plugin_was_run): service.add_manual_command('Use dnsrecon to automatically query data from the DNS server. You must specify the target domain name.', [ diff --git a/autorecon/default-plugins/oracle-scanner.py b/autorecon/default-plugins/oracle-scanner.py index c610420..16653cc 100644 --- a/autorecon/default-plugins/oracle-scanner.py +++ b/autorecon/default-plugins/oracle-scanner.py @@ -14,6 +14,7 @@ class OracleScanner(ServiceScan): def check(self): if which('oscanner') is None: self.error('The oscanner program could not be found. Make sure it is installed. (On Kali, run: sudo apt install oscanner)') + return False async def run(self, service): await service.execute('oscanner -v -s {address} -P {port} 2>&1', outfile='{protocol}_{port}_oracle_scanner.txt') diff --git a/autorecon/default-plugins/oracle-tnscmd.py b/autorecon/default-plugins/oracle-tnscmd.py index b43ec12..7ea12c1 100644 --- a/autorecon/default-plugins/oracle-tnscmd.py +++ b/autorecon/default-plugins/oracle-tnscmd.py @@ -14,6 +14,7 @@ class OracleTNScmd(ServiceScan): def check(self): if which('tnscmd10g') is None: self.error('The tnscmd10g program could not be found. Make sure it is installed. (On Kali, run: sudo apt install tnscmd10g)') + return False async def run(self, service): if service.target.ipversion == 'IPv4': diff --git a/autorecon/default-plugins/redis-cli.py b/autorecon/default-plugins/redis-cli.py index a6f6c79..74b3803 100644 --- a/autorecon/default-plugins/redis-cli.py +++ b/autorecon/default-plugins/redis-cli.py @@ -14,6 +14,7 @@ class RedisCli(ServiceScan): def check(self): if which('redis-cli') is None: self.error('The redis-cli program could not be found. Make sure it is installed. (On Kali, run: sudo apt install redis-tools)') + return False async def run(self, service): if which('redis-cli') is not None: diff --git a/autorecon/default-plugins/wkhtmltoimage.py b/autorecon/default-plugins/wkhtmltoimage.py index 7c4890a..d4d30ba 100644 --- a/autorecon/default-plugins/wkhtmltoimage.py +++ b/autorecon/default-plugins/wkhtmltoimage.py @@ -15,6 +15,7 @@ class WkHTMLToImage(ServiceScan): def check(self): if which('wkhtmltoimage') is None: self.error('The wkhtmltoimage program could not be found. Make sure it is installed. (On Kali, run: sudo apt install wkhtmltopdf)') + return False async def run(self, service): if which('wkhtmltoimage') is not None: diff --git a/autorecon/main.py b/autorecon/main.py index dbbec42..5faa654 100644 --- a/autorecon/main.py +++ b/autorecon/main.py @@ -17,7 +17,7 @@ from autorecon.io import slugify, e, fformat, cprint, debug, info, warn, error, from autorecon.plugins import Pattern, PortScan, ServiceScan, Report, AutoRecon from autorecon.targets import Target, Service -VERSION = "2.0.26" +VERSION = "2.0.27" if not os.path.exists(config['config_dir']): shutil.rmtree(config['config_dir'], ignore_errors=True, onerror=None) @@ -809,7 +809,7 @@ async def run(): else: config['plugins_dir'] = None - parser = argparse.ArgumentParser(add_help=False, description='Network reconnaissance tool to port scan and automatically enumerate services found on multiple targets.') + parser = argparse.ArgumentParser(add_help=False, allow_abbrev=False, description='Network reconnaissance tool to port scan and automatically enumerate services found on multiple targets.') parser.add_argument('targets', action='store', help='IP addresses (e.g. 10.0.0.1), CIDR notation (e.g. 10.0.0.1/24), or resolvable hostnames (e.g. foo.bar) to scan.', nargs='*') parser.add_argument('-t', '--target-file', action='store', type=str, default='', help='Read targets from file.') parser.add_argument('-p', '--ports', action='store', type=str, help='Comma separated list of ports / port ranges to scan. Specify TCP/UDP ports by prepending list with T:/U: To scan both TCP/UDP, put port(s) at start or specify B: e.g. 53,T:21-25,80,U:123,B:123. Default: %(default)s') @@ -1081,6 +1081,7 @@ async def run(): autorecon.argparse.set_defaults(**{key: val}) parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS, help='Show this help message and exit.') + parser.error = lambda s: fail(s[0].upper() + s[1:]) args = parser.parse_args() args_dict = vars(args) @@ -1138,7 +1139,7 @@ async def run(): else: error('Invalid value provided to --max-plugin-global-instances. Values must be in the format PLUGIN:NUMBER.') - for plugin in autorecon.plugins.values(): + for slug, plugin in autorecon.plugins.items(): if hasattr(plugin, 'max_target_instances') and plugin.slug in max_plugin_target_instances: plugin.max_target_instances = max_plugin_target_instances[plugin.slug] @@ -1147,7 +1148,9 @@ async def run(): for member_name, _ in inspect.getmembers(plugin, predicate=inspect.ismethod): if member_name == 'check': - plugin.check() + if plugin.check() == False: + autorecon.plugins.pop(slug) + continue continue if config['ports']: diff --git a/pyproject.toml b/pyproject.toml index ab8ec7e..9e5d658 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "autorecon" -version = "2.0.26" +version = "2.0.27" description = "A multi-threaded network reconnaissance tool which performs automated enumeration of services." authors = ["Tib3rius"] license = "GNU GPL v3"