Update autorecon.py
Added --create-port-dirs option which will create port directories (e.g. tcp80, udp53) in the scans directory and put all relevant scans there. Now most command line options can be set in the config.toml file as well.
This commit is contained in:
parent
a1ca13ecbe
commit
6843c0450f
123
autorecon.py
123
autorecon.py
|
@ -58,10 +58,10 @@ class Target:
|
|||
info('Port scan {bblue}' + plugin.name + ' (' + tag + '){rst} is running the following command against {byellow}' + address + '{rst}: ' + cmd)
|
||||
|
||||
if outfile is not None:
|
||||
outfile = os.path.abspath(os.path.join(target.scandir, e(outfile)))
|
||||
outfile = os.path.join(target.scandir, e(outfile))
|
||||
|
||||
if errfile is not None:
|
||||
errfile = os.path.abspath(os.path.join(target.scandir, e(errfile)))
|
||||
errfile = os.path.join(target.scandir, e(errfile))
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_commands.log'), 'a') as file:
|
||||
|
@ -121,6 +121,11 @@ class Service:
|
|||
port = self.port
|
||||
name = self.name
|
||||
|
||||
if target.autorecon.config['create_port_dirs']:
|
||||
scandir = os.path.join(scandir, protocol + str(port))
|
||||
os.makedirs(scandir, exist_ok=True)
|
||||
os.makedirs(os.path.join(scandir, 'xml'), exist_ok=True)
|
||||
|
||||
# Special cases for HTTP.
|
||||
http_scheme = 'https' if 'https' in self.name or self.secure is True else 'http'
|
||||
|
||||
|
@ -141,10 +146,10 @@ class Service:
|
|||
info('Service scan {bblue}' + plugin.name + ' (' + tag + '){rst} is running the following command against {byellow}' + address + '{rst}: ' + cmd)
|
||||
|
||||
if outfile is not None:
|
||||
outfile = os.path.abspath(os.path.join(target.scandir, e(outfile)))
|
||||
outfile = os.path.join(scandir, e(outfile))
|
||||
|
||||
if errfile is not None:
|
||||
errfile = os.path.abspath(os.path.join(target.scandir, e(errfile)))
|
||||
errfile = os.path.join(scandir, e(errfile))
|
||||
|
||||
async with target.lock:
|
||||
with open(os.path.join(target.scandir, '_commands.log'), 'a') as file:
|
||||
|
@ -376,18 +381,44 @@ class AutoRecon(object):
|
|||
self.tags = []
|
||||
self.excluded_tags = []
|
||||
self.patterns = []
|
||||
self.configurable_keys = ['max_scans', 'max_port_scans', 'single_target', 'outdir', 'only_scans_dir', 'heartbeat', 'timeout', 'target_timeout', 'accessible', 'verbose']
|
||||
self.configurable_keys = [
|
||||
'max_scans',
|
||||
'max_port_scans',
|
||||
'tags',
|
||||
'exclude_tags',
|
||||
'plugins_dir',
|
||||
'outdir',
|
||||
'single_target',
|
||||
'only_scans_dir',
|
||||
'create_port_dirs',
|
||||
'heartbeat',
|
||||
'timeout',
|
||||
'target_timeout',
|
||||
'nmap',
|
||||
'nmap_append',
|
||||
'disable_sanity_checks',
|
||||
'accessible',
|
||||
'verbose'
|
||||
]
|
||||
self.configurable_boolean_keys = ['single_target', 'only_scans_dir', 'create_port_dirs', 'disable_sanity_checks', 'accessible']
|
||||
self.config = {
|
||||
'protected_classes': ['autorecon', 'target', 'service', 'commandstreamreader', 'plugin', 'portscan', 'servicescan', 'global', 'pattern'],
|
||||
'global_file': os.path.dirname(os.path.realpath(__file__)) + '/global.toml',
|
||||
'max_scans': 50,
|
||||
'max_port_scans': None,
|
||||
'single_target': False,
|
||||
'tags': 'default',
|
||||
'exclude_tags': None,
|
||||
'plugins_dir': os.path.dirname(os.path.abspath(__file__)) + '/plugins',
|
||||
'outdir': 'results',
|
||||
'single_target': False,
|
||||
'only_scans_dir': False,
|
||||
'create_port_dirs': False,
|
||||
'heartbeat': 60,
|
||||
'timeout': None,
|
||||
'target_timeout': None,
|
||||
'nmap': '-vv --reason -Pn',
|
||||
'nmap_append': '',
|
||||
'disable_sanity_checks': False,
|
||||
'accessible': False,
|
||||
'verbose': 0
|
||||
}
|
||||
|
@ -806,27 +837,27 @@ async def scan_target(target):
|
|||
os.makedirs(basedir, exist_ok=True)
|
||||
|
||||
if not target.autorecon.config['only_scans_dir']:
|
||||
exploitdir = os.path.abspath(os.path.join(basedir, 'exploit'))
|
||||
exploitdir = os.path.join(basedir, 'exploit')
|
||||
os.makedirs(exploitdir, exist_ok=True)
|
||||
|
||||
lootdir = os.path.abspath(os.path.join(basedir, 'loot'))
|
||||
lootdir = os.path.join(basedir, 'loot')
|
||||
os.makedirs(lootdir, exist_ok=True)
|
||||
|
||||
reportdir = os.path.abspath(os.path.join(basedir, 'report'))
|
||||
reportdir = os.path.join(basedir, 'report')
|
||||
target.reportdir = reportdir
|
||||
os.makedirs(reportdir, exist_ok=True)
|
||||
|
||||
open(os.path.abspath(os.path.join(reportdir, 'local.txt')), 'a').close()
|
||||
open(os.path.abspath(os.path.join(reportdir, 'proof.txt')), 'a').close()
|
||||
open(os.path.join(reportdir, 'local.txt'), 'a').close()
|
||||
open(os.path.join(reportdir, 'proof.txt'), 'a').close()
|
||||
|
||||
screenshotdir = os.path.abspath(os.path.join(reportdir, 'screenshots'))
|
||||
screenshotdir = os.path.join(reportdir, 'screenshots')
|
||||
os.makedirs(screenshotdir, exist_ok=True)
|
||||
|
||||
scandir = os.path.abspath(os.path.join(basedir, 'scans'))
|
||||
scandir = os.path.join(basedir, 'scans')
|
||||
target.scandir = scandir
|
||||
os.makedirs(scandir, exist_ok=True)
|
||||
|
||||
os.makedirs(os.path.abspath(os.path.join(scandir, 'xml')), exist_ok=True)
|
||||
os.makedirs(os.path.join(scandir, 'xml'), exist_ok=True)
|
||||
|
||||
pending = []
|
||||
|
||||
|
@ -1047,24 +1078,25 @@ async def main():
|
|||
parser = argparse.ArgumentParser(add_help=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', '--targets', action='store', type=str, default='', dest='target_file', help='Read targets from file.')
|
||||
parser.add_argument('-m', '--max-scans', action='store', type=int, help='The maximum number of concurrent scans to run. Default: 50')
|
||||
parser.add_argument('-m', '--max-scans', action='store', type=int, help='The maximum number of concurrent scans to run. Default: %(default)s')
|
||||
parser.add_argument('-mp', '--max-port-scans', action='store', type=int, help='The maximum number of concurrent port scans to run. Default: 10 (approx 20%% of max-scans unless specified)')
|
||||
parser.add_argument('-c', '--config', action='store', type=str, default=os.path.dirname(os.path.realpath(__file__)) + '/config.toml', dest='config_file', help='Location of AutoRecon\'s config file. Default: %(default)s')
|
||||
parser.add_argument('-g', '--global-file', action='store', type=str, dest='global_file', help='Location of AutoRecon\'s global file. Default: ' + os.path.dirname(os.path.realpath(__file__)) + '/global.toml')
|
||||
parser.add_argument('--tags', action='store', type=str, default='default', help='Tags to determine which plugins should be included. Separate tags by a plus symbol (+) to group tags together. Separate groups with a comma (,) to create multiple groups. For a plugin to be included, it must have all the tags specified in at least one group.')
|
||||
parser.add_argument('--exclude-tags', action='store', type=str, default='', help='Tags to determine which plugins should be excluded. Separate tags by a plus symbol (+) to group tags together. Separate groups with a comma (,) to create multiple groups. For a plugin to be excluded, it must have all the tags specified in at least one group.')
|
||||
parser.add_argument('--plugins-dir', action='store', type=str, default=os.path.dirname(os.path.abspath(__file__)) + '/plugins', help='')
|
||||
parser.add_argument('-o', '--output', action='store', dest='outdir', help='The output directory for results. Default: results')
|
||||
parser.add_argument('--single-target', action='store_true', help='Only scan a single target. A directory named after the target will not be created. Instead, the directory structure will be created within the output directory. Default: false')
|
||||
parser.add_argument('--only-scans-dir', action='store_true', help='Only create the "scans" directory for results. Other directories (e.g. exploit, loot, report) will not be created. Default: false')
|
||||
parser.add_argument('--heartbeat', action='store', type=int, help='Specifies the heartbeat interval (in seconds) for scan status messages. Default: 60')
|
||||
parser.add_argument('--timeout', action='store', type=int, help='Specifies the maximum amount of time in minutes that AutoRecon should run for. Default: no timeout')
|
||||
parser.add_argument('--target-timeout', action='store', type=int, help='Specifies the maximum amount of time in minutes that a target should be scanned for before abandoning it and moving on. Default: no timeout')
|
||||
parser.add_argument('--tags', action='store', type=str, default='default', help='Tags to determine which plugins should be included. Separate tags by a plus symbol (+) to group tags together. Separate groups with a comma (,) to create multiple groups. For a plugin to be included, it must have all the tags specified in at least one group. Default: %(default)s')
|
||||
parser.add_argument('--exclude-tags', action='store', type=str, default='', help='Tags to determine which plugins should be excluded. Separate tags by a plus symbol (+) to group tags together. Separate groups with a comma (,) to create multiple groups. For a plugin to be excluded, it must have all the tags specified in at least one group. Default: %(default)s')
|
||||
parser.add_argument('--plugins-dir', action='store', type=str, help='The location of the plugins directory. Default: %(default)s')
|
||||
parser.add_argument('-o', '--output', action='store', dest='outdir', help='The output directory for results. Default: %(default)s')
|
||||
parser.add_argument('--single-target', action='store_true', help='Only scan a single target. A directory named after the target will not be created. Instead, the directory structure will be created within the output directory. Default: %(default)s')
|
||||
parser.add_argument('--only-scans-dir', action='store_true', help='Only create the "scans" directory for results. Other directories (e.g. exploit, loot, report) will not be created. Default: %(default)s')
|
||||
parser.add_argument('--create-port-dirs', action='store_true', help='Create directories for ports within the "scans" directory (e.g. scans/tcp80, scans/udp53) and store results in these directories. Default: %(default)s')
|
||||
parser.add_argument('--heartbeat', action='store', type=int, help='Specifies the heartbeat interval (in seconds) for scan status messages. Default: %(default)s')
|
||||
parser.add_argument('--timeout', action='store', type=int, help='Specifies the maximum amount of time in minutes that AutoRecon should run for. Default: %(default)s')
|
||||
parser.add_argument('--target-timeout', action='store', type=int, help='Specifies the maximum amount of time in minutes that a target should be scanned for before abandoning it and moving on. Default: %(default)s')
|
||||
nmap_group = parser.add_mutually_exclusive_group()
|
||||
nmap_group.add_argument('--nmap', action='store', default='-vv --reason -Pn', help='Override the {nmap_extra} variable in scans. Default: %(default)s')
|
||||
nmap_group.add_argument('--nmap-append', action='store', default='', help='Append to the default {nmap_extra} variable in scans.')
|
||||
parser.add_argument('--disable-sanity-checks', action='store_true', default=False, help='Disable sanity checks that would otherwise prevent the scans from running. Default: false')
|
||||
parser.add_argument('--accessible', action='store_true', help='Attempts to make AutoRecon output more accessible to screenreaders.')
|
||||
nmap_group.add_argument('--nmap', action='store', help='Override the {nmap_extra} variable in scans. Default: %(default)s')
|
||||
nmap_group.add_argument('--nmap-append', action='store', help='Append to the default {nmap_extra} variable in scans. Default: %(default)s')
|
||||
parser.add_argument('--disable-sanity-checks', action='store_true', help='Disable sanity checks that would otherwise prevent the scans from running. Default: %(default)s')
|
||||
parser.add_argument('--accessible', action='store_true', help='Attempts to make AutoRecon output more accessible to screenreaders. Default: %(default)s')
|
||||
parser.add_argument('-v', '--verbose', action='count', help='Enable verbose output. Repeat for more verbosity.')
|
||||
parser.add_argument('--version', action='store_true', help='Prints the AutoRecon version and exits.')
|
||||
parser.error = lambda s: fail(s[0].upper() + s[1:])
|
||||
|
@ -1086,9 +1118,10 @@ async def main():
|
|||
try:
|
||||
config_toml = toml.load(c)
|
||||
for key, val in config_toml.items():
|
||||
if key.replace('-', '_') == 'global_file':
|
||||
if key == 'global-file':
|
||||
autorecon.config['global_file'] = val
|
||||
break
|
||||
elif key == 'plugins-dir':
|
||||
autorecon.config['plugins_dir'] = val
|
||||
except toml.decoder.TomlDecodeError:
|
||||
fail('Error: Couldn\'t parse ' + args.config_file + ' config file. Check syntax.')
|
||||
|
||||
|
@ -1096,19 +1129,20 @@ async def main():
|
|||
for key in args_dict:
|
||||
if key == 'global_file' and args_dict['global_file'] is not None:
|
||||
autorecon.config['global_file'] = args_dict['global_file']
|
||||
break
|
||||
elif key == 'plugins_dir' and args_dict['plugins_dir'] is not None:
|
||||
autorecon.config['plugins_dir'] = args_dict['plugins_dir']
|
||||
|
||||
if not os.path.isdir(args.plugins_dir):
|
||||
fail('Error: Specified plugins directory "' + args.plugins_dir + '" does not exist.')
|
||||
if not os.path.isdir(autorecon.config['plugins_dir']):
|
||||
fail('Error: Specified plugins directory "' + autorecon.config['plugins_dir'] + '" does not exist.')
|
||||
|
||||
for plugin_file in os.listdir(args.plugins_dir):
|
||||
for plugin_file in os.listdir(autorecon.config['plugins_dir']):
|
||||
if not plugin_file.startswith('_') and plugin_file.endswith('.py'):
|
||||
|
||||
dirname, filename = os.path.split(plugin_file)
|
||||
dirname = os.path.abspath(dirname)
|
||||
|
||||
try:
|
||||
plugin = importlib.import_module('.' + filename[:-3], os.path.basename(args.plugins_dir))
|
||||
plugin = importlib.import_module('.' + filename[:-3], os.path.basename(autorecon.config['plugins_dir']))
|
||||
clsmembers = inspect.getmembers(plugin, predicate=inspect.isclass)
|
||||
for (_, c) in clsmembers:
|
||||
if c.__module__ == 'autorecon':
|
||||
|
@ -1129,7 +1163,7 @@ async def main():
|
|||
sys.exit(1)
|
||||
|
||||
if len(autorecon.plugin_types['port']) == 0:
|
||||
fail('Error: There are no valid PortScan plugins in the plugins directory "' + args.plugins_dir + '".')
|
||||
fail('Error: There are no valid PortScan plugins in the plugins directory "' + autorecon.config['plugins_dir'] + '".')
|
||||
|
||||
# Sort plugins by priority.
|
||||
autorecon.plugin_types['port'].sort(key=lambda x: x.priority)
|
||||
|
@ -1192,6 +1226,7 @@ async def main():
|
|||
except toml.decoder.TomlDecodeError:
|
||||
fail('Error: Couldn\'t parse ' + g.name + ' file. Check syntax.')
|
||||
|
||||
other_options = []
|
||||
for key, val in config_toml.items():
|
||||
if key == 'global' and isinstance(val, dict): # Process global plugin options.
|
||||
for gkey, gval in config_toml['global'].items():
|
||||
|
@ -1223,9 +1258,15 @@ async def main():
|
|||
if autorecon.argparse.get_default(slugify(key).replace('-', '_') + '.' + slugify(pkey).replace('-', '_')):
|
||||
autorecon.argparse.set_defaults(**{slugify(key).replace('-', '_') + '.' + slugify(pkey).replace('-', '_'): pval})
|
||||
else: # Process potential other options.
|
||||
if key.replace('-', '_') in autorecon.configurable_keys:
|
||||
autorecon.config[key.replace('-', '_')] = val
|
||||
autorecon.argparse.set_defaults(**{key.replace('-', '_'): val})
|
||||
key = key.replace('-', '_')
|
||||
if key in autorecon.configurable_keys:
|
||||
other_options.append(key)
|
||||
autorecon.config[key] = val
|
||||
autorecon.argparse.set_defaults(**{key: val})
|
||||
|
||||
for key, val in autorecon.config.items():
|
||||
if key not in other_options:
|
||||
autorecon.argparse.set_defaults(**{key: val})
|
||||
|
||||
parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS, help='Show this help message and exit.')
|
||||
args = parser.parse_args()
|
||||
|
@ -1234,7 +1275,7 @@ async def main():
|
|||
for key in args_dict:
|
||||
if key in autorecon.configurable_keys and args_dict[key] is not None:
|
||||
# Special case for booleans
|
||||
if key in ['accessible', 'single_target', 'only_scans_dir'] and autorecon.config[key]:
|
||||
if key in autorecon.configurable_boolean_keys and autorecon.config[key]:
|
||||
continue
|
||||
autorecon.config[key] = args_dict[key]
|
||||
|
||||
|
@ -1287,6 +1328,8 @@ async def main():
|
|||
[autorecon.tags.append(t) for t in tags if t not in autorecon.tags]
|
||||
|
||||
excluded_tags = []
|
||||
if args.exclude_tags is None:
|
||||
args.exclude_tags = ''
|
||||
if args.exclude_tags != '':
|
||||
for tag_group in list(set(filter(None, args.exclude_tags.lower().split(',')))):
|
||||
excluded_tags.append(list(set(filter(None, tag_group.split('+')))))
|
||||
|
|
Loading…
Reference in New Issue