Refactoring codebase.

Moved most of the core functionality to modules.
Plugins updates with new module name.
This commit is contained in:
Tib3rius 2021-09-01 23:52:16 -04:00
parent 0b37730304
commit 0efedca423
26 changed files with 878 additions and 854 deletions

File diff suppressed because it is too large Load Diff

0
autorecon/__init__.py Normal file
View File

59
autorecon/config.py Normal file
View File

@ -0,0 +1,59 @@
import os
configurable_keys = [
'ports',
'max_scans',
'max_port_scans',
'tags',
'exclude_tags',
'plugins_dir',
'add_plugins-dir',
'outdir',
'single_target',
'only_scans_dir',
'create_port_dirs',
'heartbeat',
'timeout',
'target_timeout',
'nmap',
'nmap_append',
'disable_sanity_checks',
'disable_keyboard_control',
'force_services',
'accessible',
'verbose'
]
configurable_boolean_keys = [
'single_target',
'only_scans_dir',
'create_port_dirs',
'disable_sanity_checks',
'accessible'
]
config = {
'protected_classes': ['autorecon', 'target', 'service', 'commandstreamreader', 'plugin', 'portscan', 'servicescan', 'global', 'pattern'],
'global_file': os.path.dirname(os.path.realpath(os.path.join(__file__, '..'))) + '/global.toml',
'ports': None,
'max_scans': 50,
'max_port_scans': None,
'tags': 'default',
'exclude_tags': None,
'plugins_dir': os.path.dirname(os.path.abspath(os.path.join(__file__, '..'))) + '/plugins',
'add_plugins_dir': None,
'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,
'disable_keyboard_control': False,
'force_services': None,
'accessible': False,
'verbose': 0
}

163
autorecon/io.py Normal file
View File

@ -0,0 +1,163 @@
import asyncio, colorama, os, re, string, sys, unidecode
from colorama import Fore, Style
from autorecon.config import config
def slugify(name):
return re.sub(r'[\W_]+', '-', unidecode.unidecode(name).lower()).strip('-')
def e(*args, frame_index=1, **kvargs):
frame = sys._getframe(frame_index)
vals = {}
vals.update(frame.f_globals)
vals.update(frame.f_locals)
vals.update(kvargs)
return string.Formatter().vformat(' '.join(args), args, vals)
def fformat(s):
return e(s, frame_index=3)
def cprint(*args, color=Fore.RESET, char='*', sep=' ', end='\n', frame_index=1, file=sys.stdout, printmsg=True, **kvargs):
frame = sys._getframe(frame_index)
vals = {
'bgreen': Fore.GREEN + Style.BRIGHT,
'bred': Fore.RED + Style.BRIGHT,
'bblue': Fore.BLUE + Style.BRIGHT,
'byellow': Fore.YELLOW + Style.BRIGHT,
'bmagenta': Fore.MAGENTA + Style.BRIGHT,
'green': Fore.GREEN,
'red': Fore.RED,
'blue': Fore.BLUE,
'yellow': Fore.YELLOW,
'magenta': Fore.MAGENTA,
'bright': Style.BRIGHT,
'srst': Style.NORMAL,
'crst': Fore.RESET,
'rst': Style.NORMAL + Fore.RESET
}
if config['accessible']:
vals = {'bgreen':'', 'bred':'', 'bblue':'', 'byellow':'', 'bmagenta':'', 'green':'', 'red':'', 'blue':'', 'yellow':'', 'magenta':'', 'bright':'', 'srst':'', 'crst':'', 'rst':''}
vals.update(frame.f_globals)
vals.update(frame.f_locals)
vals.update(kvargs)
unfmt = ''
if char is not None and not config['accessible']:
unfmt += color + '[' + Style.BRIGHT + char + Style.NORMAL + ']' + Fore.RESET + sep
unfmt += sep.join(args)
fmted = unfmt
for attempt in range(10):
try:
fmted = string.Formatter().vformat(unfmt, args, vals)
break
except KeyError as err:
key = err.args[0]
unfmt = unfmt.replace('{' + key + '}', '{{' + key + '}}')
if printmsg:
print(fmted, sep=sep, end=end, file=file)
else:
return fmted
def debug(*args, color=Fore.GREEN, sep=' ', end='\n', file=sys.stdout, **kvargs):
if verbose >= 2:
if config['accessible']:
args = ('Debug:',) + args
cprint(*args, color=color, char='-', sep=sep, end=end, file=file, frame_index=2, **kvargs)
def info(*args, sep=' ', end='\n', file=sys.stdout, **kvargs):
cprint(*args, color=Fore.BLUE, char='*', sep=sep, end=end, file=file, frame_index=2, **kvargs)
def warn(*args, sep=' ', end='\n', file=sys.stderr,**kvargs):
if config['accessible']:
args = ('Warning:',) + args
cprint(*args, color=Fore.YELLOW, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
def error(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
if config['accessible']:
args = ('Error:',) + args
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
def fail(*args, sep=' ', end='\n', file=sys.stderr, **kvargs):
if config['accessible']:
args = ('Failure:',) + args
cprint(*args, color=Fore.RED, char='!', sep=sep, end=end, file=file, frame_index=2, **kvargs)
exit(-1)
class CommandStreamReader(object):
def __init__(self, stream, target, tag, patterns=None, outfile=None):
self.stream = stream
self.target = target
self.tag = tag
self.lines = []
self.patterns = patterns or []
self.outfile = outfile
self.ended = False
# Read lines from the stream until it ends.
async def _read(self):
while True:
if self.stream.at_eof():
break
try:
line = (await self.stream.readline()).decode('utf8').rstrip()
except ValueError:
error('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} A line was longer than 64 KiB and cannot be processed. Ignoring.')
continue
if config['verbose'] >= 2:
if line != '':
info('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} ' + line.replace('{', '{{').replace('}', '}}'))
# Check lines for pattern matches.
for p in self.patterns:
matches = p.pattern.findall(line)
for match in matches:
async with self.target.lock:
with open(os.path.join(self.target.scandir, '_patterns.log'), 'a') as file:
if p.description:
if config['verbose'] >= 1:
info('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} {bmagenta}' + p.description.replace('{match}', match) + '{rst}')
file.writelines(p.description.replace('{match}', match) + '\n\n')
else:
if config['verbose'] >= 1:
info('{bright}[{yellow}' + self.target.address + '{crst}/{bgreen}' + self.tag + '{crst}]{rst} {bmagenta}Matched Pattern: ' + match + '{rst}')
file.writelines('Matched Pattern: ' + match + '\n\n')
if self.outfile is not None:
with open(self.outfile, 'a') as writer:
writer.write(line + '\n')
self.lines.append(line)
self.ended = True
# Read a line from the stream cache.
async def readline(self):
while True:
try:
return self.lines.pop(0)
except IndexError:
if self.ended:
return None
else:
await asyncio.sleep(0.1)
# Read all lines from the stream cache.
async def readlines(self):
lines = []
while True:
line = await self.readline()
if line is not None:
lines.append(line)
else:
break
return lines

337
autorecon/plugins.py Normal file
View File

@ -0,0 +1,337 @@
import asyncio, inspect, os, re, sys
from typing import final
from autorecon.config import config
from autorecon.io import slugify, error, fail, CommandStreamReader
from autorecon.targets import Service
class Pattern:
def __init__(self, pattern, description=None):
self.pattern = pattern
self.description = description
class Plugin(object):
def __init__(self):
self.name = None
self.slug = None
self.description = None
self.tags = ['default']
self.priority = 1
self.patterns = []
self.autorecon = None
self.disabled = False
@final
def add_option(self, name, default=None, help=None):
self.autorecon.add_argument(self, name, metavar='VALUE', default=default, help=help)
@final
def add_constant_option(self, name, const, default=None, help=None):
self.autorecon.add_argument(self, name, action='store_const', const=const, default=default, help=help)
@final
def add_true_option(self, name, help=None):
self.autorecon.add_argument(self, name, action='store_true', help=help)
@final
def add_false_option(self, name, help=None):
self.autorecon.add_argument(self, name, action='store_false', help=help)
@final
def add_list_option(self, name, default=None, help=None):
self.autorecon.add_argument(self, name, nargs='+', metavar='VALUE', default=default, help=help)
@final
def add_choice_option(self, name, choices, default=None, help=None):
if not isinstance(choices, list):
fail('The choices argument for ' + self.name + '\'s ' + name + ' choice option should be a list.')
self.autorecon.add_argument(self, name, choices=choices, default=default, help=help)
@final
def get_option(self, name):
# TODO: make sure name is simple.
name = self.slug.replace('-', '_') + '.' + slugify(name).replace('-', '_')
if name in vars(self.autorecon.args):
return vars(self.autorecon.args)[name]
else:
return None
@final
def get_global_option(self, name, default=None):
name = 'global.' + slugify(name).replace('-', '_')
if name in vars(self.autorecon.args):
if vars(self.autorecon.args)[name] is None:
if default:
return default
else:
return None
else:
return vars(self.autorecon.args)[name]
else:
if default:
return default
return None
@final
def get_global(self, name, default=None):
return self.get_global_option(name, default)
@final
def add_pattern(self, pattern, description=None):
try:
compiled = re.compile(pattern)
if description:
self.patterns.append(Pattern(compiled, description=description))
else:
self.patterns.append(Pattern(compiled))
except re.error:
fail('Error: The pattern "' + pattern + '" in the plugin "' + self.name + '" is invalid regex.')
class PortScan(Plugin):
def __init__(self):
super().__init__()
self.type = None
async def run(self, target):
raise NotImplementedError
class ServiceScan(Plugin):
def __init__(self):
super().__init__()
self.ports = {'tcp':[], 'udp':[]}
self.ignore_ports = {'tcp':[], 'udp':[]}
self.services = []
self.service_names = []
self.ignore_service_names = []
self.match_all_service_names_boolean = False
self.run_once_boolean = False
self.require_ssl_boolean = False
@final
def match_service(self, protocol, port, name, negative_match=False):
protocol = protocol.lower()
if protocol not in ['tcp', 'udp']:
print('Invalid protocol.')
sys.exit(1)
if not isinstance(port, list):
port = [port]
port = list(map(int, port))
if not isinstance(name, list):
name = [name]
valid_regex = True
for r in name:
try:
re.compile(r)
except re.error:
print('Invalid regex: ' + r)
valid_regex = False
if not valid_regex:
sys.exit(1)
service = {'protocol': protocol, 'port': port, 'name': name, 'negative_match': negative_match}
self.services.append(service)
@final
def match_port(self, protocol, port, negative_match=False):
protocol = protocol.lower()
if protocol not in ['tcp', 'udp']:
print('Invalid protocol.')
sys.exit(1)
else:
if not isinstance(port, list):
port = [port]
port = list(map(int, port))
if negative_match:
self.ignore_ports[protocol] = list(set(self.ignore_ports[protocol] + port))
else:
self.ports[protocol] = list(set(self.ports[protocol] + port))
@final
def match_service_name(self, name, negative_match=False):
if not isinstance(name, list):
name = [name]
valid_regex = True
for r in name:
try:
re.compile(r)
except re.error:
print('Invalid regex: ' + r)
valid_regex = False
if valid_regex:
if negative_match:
self.ignore_service_names = list(set(self.ignore_service_names + name))
else:
self.service_names = list(set(self.service_names + name))
else:
sys.exit(1)
@final
def require_ssl(self, boolean):
self.require_ssl_boolean = boolean
@final
def run_once(self, boolean):
self.run_once_boolean = boolean
@final
def match_all_service_names(self, boolean):
self.match_all_service_names_boolean = boolean
class AutoRecon(object):
def __init__(self):
self.pending_targets = []
self.scanning_targets = []
self.plugins = {}
self.__slug_regex = re.compile('^[a-z0-9\-]+$')
self.plugin_types = {'port':[], 'service':[]}
self.port_scan_semaphore = None
self.service_scan_semaphore = None
self.argparse = None
self.argparse_group = None
self.args = None
self.missing_services = []
self.taglist = []
self.tags = []
self.excluded_tags = []
self.patterns = []
self.errors = False
self.lock = asyncio.Lock()
self.load_slug = None
self.load_module = None
def add_argument(self, plugin, name, **kwargs):
# TODO: make sure name is simple.
name = '--' + plugin.slug + '.' + slugify(name)
if self.argparse_group is None:
self.argparse_group = self.argparse.add_argument_group('plugin arguments', description='These are optional arguments for certain plugins.')
self.argparse_group.add_argument(name, **kwargs)
def extract_service(self, line, regex):
if regex is None:
regex = '^(?P<port>\d+)\/(?P<protocol>(tcp|udp))(.*)open(\s*)(?P<service>[\w\-\/]+)(\s*)(.*)$'
match = re.search(regex, line)
if match:
protocol = match.group('protocol').lower()
port = int(match.group('port'))
service = match.group('service')
secure = True if 'ssl' in service or 'tls' in service else False
if service.startswith('ssl/') or service.startswith('tls/'):
service = service[4:]
return Service(protocol, port, service, secure)
else:
return None
async def extract_services(self, stream, regex):
if not isinstance(stream, CommandStreamReader):
print('Error: extract_services must be passed an instance of a CommandStreamReader.')
sys.exit(1)
services = []
while True:
line = await stream.readline()
if line is not None:
service = self.extract_service(line, regex)
if service:
services.append(service)
else:
break
return services
def register(self, plugin, filename):
if plugin.disabled:
return
for _, loaded_plugin in self.plugins.items():
if plugin.name == loaded_plugin.name:
fail('Error: Duplicate plugin name "' + plugin.name + '" detected in ' + filename + '.', file=sys.stderr)
if plugin.slug is None:
plugin.slug = slugify(plugin.name)
elif not self.__slug_regex.match(plugin.slug):
fail('Error: provided slug "' + plugin.slug + '" in ' + filename + ' is not valid (must only contain lowercase letters, numbers, and hyphens).', file=sys.stderr)
if plugin.slug in config['protected_classes']:
fail('Error: plugin slug "' + plugin.slug + '" in ' + filename + ' is a protected string. Please change.')
if plugin.slug not in self.plugins:
for _, loaded_plugin in self.plugins.items():
if plugin is loaded_plugin:
fail('Error: plugin "' + plugin.name + '" in ' + filename + ' already loaded as "' + loaded_plugin.name + '" (' + str(loaded_plugin) + ')', file=sys.stderr)
configure_function_found = False
run_coroutine_found = False
manual_function_found = False
for member_name, member_value in inspect.getmembers(plugin, predicate=inspect.ismethod):
if member_name == 'configure':
configure_function_found = True
elif member_name == 'run' and inspect.iscoroutinefunction(member_value):
if len(inspect.getfullargspec(member_value).args) != 2:
fail('Error: the "run" coroutine in the plugin "' + plugin.name + '" in ' + filename + ' should have two arguments.', file=sys.stderr)
run_coroutine_found = True
elif member_name == 'manual':
if len(inspect.getfullargspec(member_value).args) != 3:
fail('Error: the "manual" function in the plugin "' + plugin.name + '" in ' + filename + ' should have three arguments.', file=sys.stderr)
manual_function_found = True
if not run_coroutine_found and not manual_function_found:
fail('Error: the plugin "' + plugin.name + '" in ' + filename + ' needs either a "manual" function, a "run" coroutine, or both.', file=sys.stderr)
#from autorecon import PortScan, ServiceScan
if issubclass(plugin.__class__, PortScan):
self.plugin_types["port"].append(plugin)
elif issubclass(plugin.__class__, ServiceScan):
self.plugin_types["service"].append(plugin)
else:
fail('Plugin "' + plugin.name + '" in ' + filename + ' is neither a PortScan nor a ServiceScan.', file=sys.stderr)
plugin.tags = [tag.lower() for tag in plugin.tags]
# Add plugin tags to tag list.
[self.taglist.append(t) for t in plugin.tags if t not in self.tags]
plugin.autorecon = self
if configure_function_found:
plugin.configure()
self.plugins[plugin.slug] = plugin
else:
fail('Error: plugin slug "' + plugin.slug + '" in ' + filename + ' is already assigned.', file=sys.stderr)
async def execute(self, cmd, target, tag, patterns=None, outfile=None, errfile=None):
if patterns:
combined_patterns = self.patterns + patterns
else:
combined_patterns = self.patterns
process = await asyncio.create_subprocess_shell(
cmd,
stdin=open('/dev/null'),
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
cout = CommandStreamReader(process.stdout, target, tag, patterns=combined_patterns, outfile=outfile)
cerr = CommandStreamReader(process.stderr, target, tag, patterns=combined_patterns, outfile=errfile)
asyncio.create_task(cout._read())
asyncio.create_task(cerr._read())
return process, cout, cerr

172
autorecon/targets.py Normal file
View File

@ -0,0 +1,172 @@
import asyncio, inspect, os
from typing import final
from autorecon.config import config
from autorecon.io import e, info
class Target:
def __init__(self, address, ipversion, type, autorecon):
self.address = address
self.ipversion = ipversion
self.type = type
self.autorecon = autorecon
self.basedir = ''
self.reportdir = ''
self.scandir = ''
self.lock = asyncio.Lock()
self.ports = None
self.pending_services = []
self.services = []
self.scans = []
self.running_tasks = {}
async def add_service(self, service):
async with self.lock:
self.pending_services.append(service)
def extract_service(self, line, regex=None):
return self.autorecon.extract_service(line, regex)
async def extract_services(self, stream, regex=None):
return await self.autorecon.extract_services(stream, regex)
async def execute(self, cmd, blocking=True, outfile=None, errfile=None):
target = self
# Create variables for command references.
address = target.address
addressv6 = target.address
scandir = target.scandir
nmap_extra = target.autorecon.args.nmap
if target.autorecon.args.nmap_append:
nmap_extra += ' ' + target.autorecon.args.nmap_append
if target.ipversion == 'IPv6':
nmap_extra += ' -6'
addressv6 = '[' + addressv6 + ']'
plugin = inspect.currentframe().f_back.f_locals['self']
cmd = e(cmd)
tag = plugin.slug
if config['verbose'] >= 1:
info('Port scan {bblue}' + plugin.name + ' {green}(' + tag + '){rst} is running the following command against {byellow}' + address + '{rst}: ' + cmd)
if outfile is not None:
outfile = os.path.join(target.scandir, e(outfile))
if errfile is not None:
errfile = os.path.join(target.scandir, e(errfile))
async with target.lock:
with open(os.path.join(target.scandir, '_commands.log'), 'a') as file:
file.writelines(cmd + '\n\n')
process, stdout, stderr = await target.autorecon.execute(cmd, target, tag, patterns=plugin.patterns, outfile=outfile, errfile=errfile)
target.running_tasks[tag]['processes'].append({'process': process, 'stderr': stderr, 'cmd': cmd})
# If process should block, sleep until stdout and stderr have finished.
if blocking:
while (not (stdout.ended and stderr.ended)):
await asyncio.sleep(0.1)
await process.wait()
return process, stdout, stderr
class Service:
def __init__(self, protocol, port, name, secure=False):
self.target = None
self.protocol = protocol.lower()
self.port = int(port)
self.name = name
self.secure = secure
self.manual_commands = {}
@final
def tag(self):
return self.protocol + '/' + str(self.port) + '/' + self.name
@final
def full_tag(self):
return self.protocol + '/' + str(self.port) + '/' + self.name + '/' + ('secure' if self.secure else 'insecure')
@final
def add_manual_commands(self, description, commands):
if not isinstance(commands, list):
commands = [commands]
if description not in self.manual_commands:
self.manual_commands[description] = []
# Merge in new unique commands, while preserving order.
[self.manual_commands[description].append(m) for m in commands if m not in self.manual_commands[description]]
@final
def add_manual_command(self, description, command):
self.add_manual_commands(description, command)
@final
async def execute(self, cmd, blocking=True, outfile=None, errfile=None):
target = self.target
# Create variables for command references.
address = target.address
addressv6 = target.address
scandir = target.scandir
protocol = self.protocol
port = self.port
name = self.name
if 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'
nmap_extra = target.autorecon.args.nmap
if target.autorecon.args.nmap_append:
nmap_extra += ' ' + target.autorecon.args.nmap_append
if protocol == 'udp':
nmap_extra += ' -sU'
if target.ipversion == 'IPv6':
nmap_extra += ' -6'
addressv6 = '[' + addressv6 + ']'
plugin = inspect.currentframe().f_back.f_locals['self']
cmd = e(cmd)
tag = self.tag() + '/' + plugin.slug
if config['verbose'] >= 1:
info('Service scan {bblue}' + plugin.name + ' {green}(' + tag + '){rst} is running the following command against {byellow}' + address + '{rst}: ' + cmd)
if outfile is not None:
outfile = os.path.join(scandir, e(outfile))
if errfile is not None:
errfile = os.path.join(scandir, e(errfile))
async with target.lock:
with open(os.path.join(target.scandir, '_commands.log'), 'a') as file:
file.writelines(cmd + '\n\n')
process, stdout, stderr = await target.autorecon.execute(cmd, target, tag, patterns=plugin.patterns, outfile=outfile, errfile=errfile)
target.running_tasks[tag]['processes'].append({'process': process, 'stderr': stderr, 'cmd': cmd})
# If process should block, sleep until stdout and stderr have finished.
if blocking:
while (not (stdout.ended and stderr.ended)):
await asyncio.sleep(0.1)
await process.wait()
return process, stdout, stderr

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapMongoDB(ServiceScan): class NmapMongoDB(ServiceScan):

View File

@ -1,4 +1,5 @@
from autorecon import PortScan, error from autorecon.plugins import PortScan
from autorecon.io import error
import os import os
class QuickTCPPortScan(PortScan): class QuickTCPPortScan(PortScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapDNS(ServiceScan): class NmapDNS(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapFTP(ServiceScan): class NmapFTP(ServiceScan):

View File

@ -1,4 +1,5 @@
from autorecon import PortScan, Service from autorecon.plugins import PortScan
from autorecon.targets import Service
import re import re
class GuesPortScan(PortScan): class GuesPortScan(PortScan):

View File

@ -1,4 +1,5 @@
from autorecon import ServiceScan, error, info, fformat from autorecon.plugins import ServiceScan
from autorecon.io import error, info, fformat
from shutil import which from shutil import which
import os import os

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapKerberos(ServiceScan): class NmapKerberos(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapLDAP(ServiceScan): class NmapLDAP(ServiceScan):

View File

@ -1,4 +1,5 @@
from autorecon import ServiceScan, fformat from autorecon.plugins import ServiceScan
from autorecon.io import fformat
class NmapCassandra(ServiceScan): class NmapCassandra(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapNFS(ServiceScan): class NmapNFS(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapRDP(ServiceScan): class NmapRDP(ServiceScan):

View File

@ -1,4 +1,5 @@
from autorecon import ServiceScan, error from autorecon.plugins import ServiceScan
from autorecon.io import error
from shutil import which from shutil import which
class NmapRedis(ServiceScan): class NmapRedis(ServiceScan):

View File

@ -1,4 +1,5 @@
from autorecon import ServiceScan, error, warn from autorecon.plugins import ServiceScan
from autorecon.io import error, warn
from shutil import which from shutil import which
class NmapRPC(ServiceScan): class NmapRPC(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapRsync(ServiceScan): class NmapRsync(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapSIP(ServiceScan): class NmapSIP(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapSMB(ServiceScan): class NmapSMB(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapSMTP(ServiceScan): class NmapSMTP(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapSNMP(ServiceScan): class NmapSNMP(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class NmapSSH(ServiceScan): class NmapSSH(ServiceScan):

View File

@ -1,4 +1,4 @@
from autorecon import ServiceScan from autorecon.plugins import ServiceScan
class SSLScan(ServiceScan): class SSLScan(ServiceScan):