Пример #1
0
def run():
    log = Log(name='main ')

    # parse start arguments
    args = ArgPars()
    args()

    # display current version
    if args.version:
        print(f'Gummy version {gummy.__version__} (https://github.com/v-yar/gummy)')
        sys.exit()

    # get default config
    config = Config()

    # create a default configuration if required
    if args.create_default_config:
        config.create_default_config()
        sys.exit()
    config.read_default_config()

    # set logger settings
    log.initialization(config.default_config['LOGING'])
    db = Storage()
    scanner = Scanner(db)
    log.info(f'Start time: {datetime.datetime.now().strftime("%Y.%m.%d %H:%M:%S")}')

    # set workspace name
    if args.workspase is None:
        workspace = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
    else:
        workspace = args.workspase
    config.start_config.set('MAIN', 'workspase', workspace)
    config + args.to_dict()

    # set current network cidr
    if args.target == 'auto':
        config.start_config.set('MASSCAN', 'target', get_ip())

    # start shell
    shell = GummyShell(config=config, db=db, scanner=scanner)
    shell()
Пример #2
0
class Config:
    """class for managing script configurations."""
    def __init__(self):
        """initializing the configuration class."""
        DIR = Path(
            os.path.abspath(os.path.join(os.path.dirname(__file__), '../')))
        self.log = Log(name='conf ')
        self.default_config = None
        self.start_config_path = None
        self.start_config = None

        self.default_config_path = os.path.join(DIR, 'setting.ini')
        self.DEFAULT_CONFIG = {
            'MAIN': {
                '# Contains the main application settings':
                None,
                '# Path:':
                None,
                'result_path':
                Path(os.path.abspath(os.path.join(DIR, '../', 'scans'))),
                'masscan_path':
                '/usr/bin/masscan',
                'nmap_path':
                '/usr/bin/nmap',
                '# Reporting:':
                None,
                'rep_type':
                'None'
            },
            'LOGING': {
                '# Contains the logging settings':
                None,
                '# Logging:':
                None,
                '# log_level: DEBUG, INFO, WARNING, ERROR, CRITICAL':
                None,
                '# log_format: https://docs.python.org/3/library/logging.html#logrecord-attributes':
                None,
                '# log_format_date: ‘%Y-%m-%d %H:%M:%S,uuu’':
                None,
                'log_level':
                'INFO',
                'log_format':
                '%(asctime)s | %(name)s | %(levelname)s | %(message)s',
                'log_format_date':
                '%H:%M:%S',
                'log_file_path':
                os.path.join(DIR, 'log')
            },
            'CONSTANTS': {
                '# Contains non-modifiable values': None,
                'private_cidr': '10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16'
            },
            'MASSCAN': {
                '# The first step - hosts discovery': None,
                'target': '127.0.0.1',
                'target_exclude': '',
                'port': '',
                'top_ports': '',
                'rate': '10000'
            },
            'NMAP': {
                '# The second step - detailed scanning of discovered hosts':
                None,
                'scan_type': 'basic'
            },
        }

    def __add__(self, other):
        """
        this method allows you to update the workload configuration data with startup arguments.
        :param other: argument dictionary of the required format.
        """
        for key in list(other.keys()):
            self.start_config[key].update(other[key])
            self.log.debug(list(self.start_config[key].values()))

    def create_default_config(self):
        """method for creating a basic configuration file."""
        config = configparser.RawConfigParser(allow_no_value=True,
                                              delimiters='=')
        config.read_dict(self.DEFAULT_CONFIG)

        with open(self.default_config_path, 'w') as config_file:
            config.write(config_file)
            self.log.info(
                f'Default configuration file {self.default_config_path} successfully created'
            )

    def read_default_config(self):
        """method for read a basic configuration file."""
        if not os.path.exists(self.default_config_path):
            self.log.info(
                f'The configuration file {self.default_config_path} was not found.'
            )
            self.create_default_config()

        self.log.info('Read configuration file')
        try:
            config = configparser.RawConfigParser()
            config.read(self.default_config_path)
            self.log.info(
                f'Default configuration file {self.default_config_path} successfully read'
            )
        except Exception:
            self.log.warning(
                f'Default configuration file {self.default_config_path} incorrect!'
            )
            raise Exception()

        self.default_config = config
        self.start_config = config

    def read_start_config(self, file):
        """method for read a basic configuration file."""
        if not os.path.exists(file):
            self.log.warning(f'The configuration file {file} was not found!')
        else:
            self.log.info('Read configuration file')
            try:
                config = configparser.RawConfigParser()
                config.read(file)
                self.log.info(f'Configuration file {file} successfully read')
            except Exception:
                self.log.warning(f'Configuration file {file} incorrect!')
                raise Exception()

            self.default_config = config
            self.start_config = config

    def create_start_config(self):
        """method for writing start parameters to the startup configuration file."""
        with open(self.start_config_path, 'w') as config_file:
            self.start_config.write(config_file)
            self.log.info(
                f'Startup configuration file {self.start_config_path} successfully created'
            )

    @property
    def get_start_config(self):
        """method for print start config in console"""
        list_start_config = list()
        for section in self.start_config.sections():
            section_pr = section
            for key in self.start_config[section]:
                list_start_config.append(
                    [section_pr, key, self.start_config[section][key]])
                section_pr = ''

        return list_start_config

    def set_start_config_key(self, key, value):
        """method for changing parameter about current configuration"""
        for section in self.start_config.sections():
            for k in self.start_config[section]:
                if k == key:
                    self.start_config.set(section, k, value)
                    return True
        return False

    def get_start_config_key(self, key):
        """method for getting current cinf parameter"""
        for section in self.start_config.sections():
            for k in self.start_config[section]:
                if k == key:
                    return self.start_config[section][k]
        return 'no key'

    def get_all_start_config_key(self):
        """method for generating a list of possible configuration parameters (for shell completer)"""
        keys = dict()
        for section in self.start_config.sections():
            for key in self.start_config[section]:
                keys[key] = ''
        return keys
Пример #3
0
class GummyShell:
    """Forms the cli control interface of the scanner"""
    def __init__(self, config, db, scanner):
        """class object initialization"""
        self.log = Log(name='shell')
        self.config = config
        self.db = db
        self.scan = scanner
        self.parser = Parser()
        self.collors = (
            '#000000',
            '#800000',
            '#008000',
            '#808000',
            '#000080',
            '#800080',
            '#008080',
            '#c0c0c0',
            '#808080',
            '#ff0000',
            '#00ff00',
            '#ffff00',
            '#0000ff',
            '#ff00ff',
            '#00ffff',
            '#ffffff',
            '#000000',
            '#00005f',
            '#000087',
            '#0000af',
            '#0000d7',
            '#0000ff',
            '#005f00',
            '#005f5f',
            '#005f87',
            '#005faf',
            '#005fd7',
            '#005fff',
            '#008700',
            '#00875f',
            '#008787',
            '#0087af',
            '#0087d7',
            '#0087ff',
            '#00af00',
            '#00af5f',
            '#00af87',
            '#00afaf',
            '#00afd7',
            '#00afff',
            '#00d700',
            '#00d75f',
            '#00d787',
            '#00d7af',
            '#00d7d7',
            '#00d7ff',
            '#00ff00',
            '#00ff5f',
            '#00ff87',
            '#00ffaf',
            '#00ffd7',
            '#00ffff',
            '#5f0000',
            '#5f005f',
            '#5f5fd7',
            '#5faf5f',
            '#5f0087',
            '#5f00af',
            '#5f00d7',
            '#5f00ff',
            '#5f5f00',
            '#5f5f5f',
            '#5f5f87',
            '#5f5faf',
            '#5f5fff',
            '#5f8700',
            '#5f875f',
            '#5f8787',
            '#5f87af',
            '#5f87d7',
            '#5f87ff',
            '#5faf00',
            '#5faf87',
            '#5fafaf',
            '#5fafd7',
            '#5fafff',
            '#5fd700',
            '#5fd75f',
            '#5fd787',
            '#5fd7af',
            '#5fd7ff',
            '#5fff00',
            '#5fff5f',
            '#5fff87',
            '#5fffaf',
            '#5fffd7',
            '#5fffff',
            '#870000',
            '#870087',
            '#8700af',
            '#8700d7',
            '#8700ff',
            '#875f00',
            '#875f5f',
            '#875f87',
            '#875faf',
            '#875fff',
            '#878700',
            '#87875f',
            '#878787',
            '#8787af',
            '#8787d7',
            '#8787ff',
            '#87af00',
            '#87af87',
            '#87afaf',
            '#87afd7',
            '#87afff',
            '#87d700',
            '#87d75f',
            '#87d787',
            '#87d7af',
            '#87d7ff',
            '#87ff00',
            '#87ff5f',
            '#87ff87',
            '#87ffaf',
            '#87ffd7',
            '#87ffff',
            '#af0000',
            '#af0087',
            '#af00af',
            '#af00d7',
            '#af00ff',
            '#af5f00',
            '#af5f5f',
            '#af5f87',
            '#af5faf',
            '#af5fff',
            '#af8700',
            '#af875f',
            '#af8787',
            '#af87af',
            '#af87d7',
            '#af87ff',
            '#afaf00',
            '#afaf87',
            '#afafaf',
            '#afafd7',
            '#afafff',
            '#afd700',
            '#afd75f',
            '#afd787',
            '#afd7af',
            '#afd7ff',
            '#afff00',
            '#afff5f',
            '#afff87',
            '#afffaf',
            '#afffd7',
            '#afffff',
            '#d70000',
            '#d70087',
            '#d700af',
            '#d700d7',
            '#d700ff',
            '#d75f00',
            '#d75f5f',
            '#d75f87',
            '#d75faf',
            '#d75fff',
            '#d78700',
            '#d7875f',
            '#d78787',
            '#d787af',
            '#d787d7',
            '#d787ff',
            '#d7af00',
            '#d7af87',
            '#d7afaf',
            '#d7afd7',
            '#d7afff',
            '#d7d700',
            '#d7d75f',
            '#d7d787',
            '#d7d7af',
            '#d7d7ff',
            '#d7ff00',
            '#d7ff5f',
            '#d7ff87',
            '#d7ffaf',
            '#d7ffd7',
            '#d7ffff',
            '#ff0000',
            '#ff0087',
            '#ff00af',
            '#ff00d7',
            '#ff00ff',
            '#ff5f00',
            '#ff5f5f',
            '#ff5f87',
            '#ff5faf',
            '#ff5fff',
            '#ff8700',
            '#ff875f',
            '#ff8787',
            '#ff87af',
            '#ff87d7',
            '#ff87ff',
            '#ffaf00',
            '#ffaf87',
            '#ffafaf',
            '#ffafd7',
            '#ffafff',
            '#ffd700',
            '#ffd75f',
            '#ffd787',
            '#ffd7af',
            '#ffd7ff',
            '#ffff00',
            '#ffff5f',
            '#ffff87',
            '#ffffaf',
            '#ffffd7',
            '#ffffff',
            '#080808',
            '#1c1c1c',
            '#262626',
            '#303030',
            '#3a3a3a',
            '#444444',
            '#4e4e4e',
            '#585858',
            '#626262',
            '#767676',
            '#808080',
            '#8a8a8a',
            '#949494',
            '#9e9e9e',
            '#a8a8a8',
            '#b2b2b2',
            '#bcbcbc',
            '#d0d0d0',
            '#dadada',
            '#e4e4e4',
            '#eeeeee',
            '#5fd7d7',
            '#87005f',
            '#875fd7',
            '#875fd7',
            '#87af5f',
            '#87d7d7',
            '#af005f',
            '#af5fd7',
            '#afaf5f',
            '#afd7d7',
            '#d7005f',
            '#d75fd7',
            '#d7af5f',
            '#d7d7d7',
            '#ff005f',
            '#ff5fd7',
            '#ffaf5f',
            '#ffd7d7',
            '#121212',
            '#6c6c6c',
            '#c6c6c6',
        )

        self.commands = {
            'set': self.config.get_all_start_config_key(),
            'show': {
                'config': 'print curent config (takes param)',
                'host': 'print host table (takes param)',
                'port': 'print port table',
                'task': 'print running tasks',
                'log': 'print the last n lines of the log file'
            },
            'sync': {
                'config': 'synchronizes the configuration file'
            },
            'run': self.get_scanner_methods(self.scan),
            'workspase': self.get_all_workspase(),
            'flush': {},
            'kill': {},
            'help': {},
            'exit': {}
        }
        self.c_function = {
            'set': self.f_set,
            'show': self.f_show,
            'sync': self.f_sync,
            'run': self.f_run,
            'workspase': self.f_workspase,
            'flush': self.f_flush,
            'kill': self.f_kill,
            'help': self.f_help,
            'exit': self.f_exit
        }
        self.grammar = compile("""
            (\s*  (?P<command>[a-z]+)   \s*) |
            (\s*  (?P<command>[a-z]+)   \s+   (?P<operator>[A-Za-z0-9_-]+)  \s*) |
            (\s*  (?P<command>[a-z]+)   \s+   (?P<operator>[A-Za-z0-9_-]+)  \s+  (?P<parameter>[A-Za-z0-9.,-_/+*]+) \s*)
                            """)
        self.style = Style.from_dict({
            'command': '#216f21 bold',
            'operator': '#6f216f bold',
            'parameter': '#ff0000 bold',
            'trailing-input': 'bg:#662222 #ffffff',
            'bottom-toolbar': '#6f216f bg:#ffffff',
            # Logo.
            'bear': random.choice(self.collors),
            'text': random.choice(self.collors),
            # User input (default text).
            '': '#ff0066',
            # Prompt.
            'prompt_for_input': '#6f216f',
        })
        self.lexer = GrammarLexer(self.grammar,
                                  lexers={
                                      'command': SimpleLexer('class:command'),
                                      'operator':
                                      SimpleLexer('class:operator'),
                                      'parameter':
                                      SimpleLexer('class:parameter')
                                  })
        self.completer = GCompleter(self.commands)
        self.history_path = Path(
            os.path.abspath(
                os.path.join(os.path.dirname(__file__), '../.history')))
        self.history = FileHistory(self.history_path)
        version_str = ''.join(['v', gummy.__version__, ' '])
        self.logo = HTML(f'''
        <text>                                      </text><bear>    _     _   </bear>
        <text>   _____ _    _ __  __ __  ____     __</text><bear>   (c).-.(c)  </bear>
        <text>  / ____| |  | |  \/  |  \/  \ \   / /</text><bear>    / ._. \   </bear>
        <text> | |  __| |  | | \  / | \  / |\ \_/ / </text><bear>  __\( Y )/__ </bear>
        <text> | | |_ | |  | | |\/| | |\/| | \   /  </text><bear> (_.-/'-'\-._)</bear>
        <text> | |__| | |__| | |  | | |  | |  | |   </text><bear>    || </bear><text>G</text><bear> ||   </bear>
        <text>  \_____|\____/|_|  |_|_|  |_|  |_|   </text><bear>  _.' `-' '._ </bear>
        <text>                                      </text><bear> (.-./`-'\.-.)</bear>
        <text>{version_str:>38}</text><bear>  `-'      `-'</bear>
        ''')
        self.prompt_str = [('class:prompt_for_input', '>>> ')]
        self.counter = 0
        self.sync_config_stat = 0
        # 0 - never synchronized
        # 1 - changed but not synchronized
        # 7 - synchronized

    # user-invoked function block:

    def f_show(self, **kwargs):
        def show_port(self):
            for line in str(self.db.get_ports_info()).split('\n'):
                self.log.info(line)

        def show_task(self):
            for item_task in asyncio.Task.all_tasks():
                self.log.info('-' * 50)
                self.log.info(item_task)

        def show_log(self, pr):
            pr = int(pr) if pr else 100
            try:
                line_need = int(pr)
            except ValueError:
                self.log.info('use int in param')
                line_need = 100
            with open(
                    self.config.start_config['LOGING']['log_file_path']) as lp:
                log = lp.read().split('\n')
                print('-' * 8)
                for ind, line in enumerate(log):
                    if len(log) - line_need <= ind:
                        print(f'log {ind:4}|  {line}')
                print('-' * 8)

        def show_config(self, pr):
            if pr:
                vl = self.config.get_start_config_key(key=pr)
                self.log.info(f'{pr}: {vl}')
            else:
                table = PrettyTable()
                table.field_names = ['SECTOR', 'KEY', 'VALUE']
                table.align = 'l'
                table.align['SECTOR'] = 'c'
                conf = self.config.get_start_config
                for item in conf:
                    table.add_row(item)
                for line in str(table).split('\n'):
                    self.log.info(line)

        def show_host(self, pr):
            if pr:
                pp = pprint.PrettyPrinter(width=80)

                pr = pr.replace('*', '[\d.]*')
                pr = pr.replace('+', '[\d.]+')
                pr = ''.join([pr, '$'])
                try:
                    regex = re.compile(pr)
                except Exception:
                    self.log.warning('Invalid regexp')
                else:
                    for host in self.db.data:
                        try:
                            search = regex.search(host['addr'])
                        except Exception:
                            search = False
                            self.log.warning('Invalid regexp')
                        if search:
                            for line in pp.pformat(host).split('\n'):
                                self.log.info(line)
            else:
                for line in str(self.db.get_table()).split('\n'):
                    self.log.info(line)

        if kwargs.get('operator'):
            op = kwargs.get('operator')
            if op == 'config':
                show_config(self, pr=kwargs.get('parameter'))
            elif op == 'log':
                show_log(self, pr=kwargs.get('parameter'))
            elif op == 'host':
                show_host(self, pr=kwargs.get('parameter'))
            elif op == 'task':
                show_task(self)
            elif op == 'port':
                show_port(self)
        else:
            self.log.info('What to show?')
            self.log.info(', '.join(self.commands.get('show')))

    def f_sync(self, **kwargs):
        if kwargs.get('operator'):
            op = kwargs.get('operator')
            if op == 'config':
                # create workspace folders
                result_path = self.config.default_config.get(
                    "MAIN", "result_path")
                workspace = self.config.start_config['MAIN']['workspase']
                workspace_path = '/'.join([result_path, workspace])
                self.config.start_config.set('MAIN', 'workspace_path',
                                             workspace_path)
                start_config_path = '/'.join(
                    [workspace_path, 'start_config.ini'])
                self.config.start_config.set('MAIN', 'start_config_path',
                                             start_config_path)
                mk_dir(result_path)
                mk_dir(workspace_path)
                # create starting config file
                self.config.start_config_path = start_config_path
                self.config.create_start_config()
                # sync gummy gummy_scan cinf
                self.scan.sync(self.config.start_config)

                self.sync_config_stat = 7
        else:
            self.log.info('What to sync?')
            self.log.info(', '.join(self.commands.get('sync')))

    def f_set(self, **kwargs):
        if kwargs.get('operator'):
            op = kwargs.get('operator')
            if kwargs.get('parameter'):
                pr = kwargs.get('parameter')
            else:
                pr = ''
            self.sync_config_stat = 1
            self.config.set_start_config_key(key=op, value=pr)

        else:
            self.log.info('What to set?')
            self.log.info(', '.join(self.commands.get('set')))

    def f_run(self, **kwargs):
        if self.sync_config_stat == 1:
            self.log.info('configuration changed but not synchronized!')

        if self.sync_config_stat in [0, 1]:
            self.log.info('automatic synchronization start')
            self.f_sync(operator='config')

        if kwargs.get('operator'):
            op = '_' + kwargs.get('operator')
            getattr(self.scan, op)()
        else:
            self.log.info('What to run?')
            self.log.info(', '.join(self.commands.get('run')))

    def f_workspase(self, **kwargs):
        if kwargs.get('operator'):
            op = kwargs.get('operator')
            result_path = self.config.start_config['MAIN']['result_path']
            workspase_path = f'{result_path}/{op}'
            self.log.info(f'Ok loding {result_path}/{op}')

            counter = self.get_max_scans(workspase_path)
            self.log.info(f'Set gummy_scan counter is: {counter}')
            self.scan.counter = counter

            workspase_config_path = f'{workspase_path}/start_config.ini'
            self.log.info(f'Read workspase config: {workspase_config_path}')
            self.config.read_start_config(file=workspase_config_path)

            self.log.info('Load gummy_scan results:')
            for scaner in ['m', 'n']:
                for file in self.get_xml_files(scan_path=workspase_path,
                                               scaner=scaner):
                    self.log.info(f' -- {file}')
                    self.parser(file)
                    self.db + self.parser.result

        else:
            self.log.info('What workspace to load?')
            self.log.info(', '.join(self.get_all_workspase()))

    def f_kill(self):
        for item_task in asyncio.Task.all_tasks():
            if '<Task pending coro=<GummyShell.start()' not in str(item_task):
                self.log.info('-' * 50)
                self.log.info(item_task)
                self.log.info(item_task.cancel())

    def f_flush(self):
        scan_path = self.config.start_config['MAIN']['result_path']
        log_path = self.config.start_config['LOGING']['log_file_path']

        list_scans = os.listdir(path=scan_path)

        self.log.info('clear log file...')
        os.truncate(log_path, 0)
        self.log.info('clear history file...')
        os.truncate(self.history_path, 0)
        for scan in list_scans:
            current_path = f'{scan_path}/{scan}'
            self.log.info(f'remove gummy_scan: {current_path}')
            shutil.rmtree(current_path, ignore_errors=True)

        self.f_exit()

    def f_help(self):
        self.log.info('No one will help you.')

    def f_exit(self):
        self.log.info('...')
        raise EOFError

    @staticmethod
    def get_max_scans(path):
        """workspase function, updates the gummy_scan counter"""
        xml_files = glob.glob(pathname=f'{path}/[0-9][0-9][0-9]-[nm]-*.xml')
        regex = re.compile(f'^{path}/(?P<num>[0-9]{"{3}"}).*$')
        nums = [0]
        for file in xml_files:
            remach = regex.match(file)
            if remach:
                nums.append(int(remach.group('num')))
        return max(nums)

    @staticmethod
    def get_xml_files(scan_path, scaner):
        """workspase function, getting all gummy_scan results in a directory"""
        xml_files = glob.glob(
            pathname=f'{scan_path}/[0-9][0-9][0-9]-{scaner}-*.xml')
        xml_files.sort()
        return xml_files

    def get_all_workspase(self):
        """workspase function, used to generate shell subcommands for workspase command"""
        result_path = self.config.start_config['MAIN']['result_path']
        commands = dict()

        if os.path.exists(result_path):
            subfolders = [
                f.path for f in os.scandir(result_path) if f.is_dir()
            ]

            for i, w in enumerate(subfolders):
                w_name = w[len(result_path) + 1:]
                m_len = len(self.get_xml_files(scan_path=w, scaner='m'))
                n_len = len(self.get_xml_files(scan_path=w, scaner='n'))
                commands[w_name] = f'scans: m[{m_len}], n[{n_len}]'
        return commands

    @staticmethod
    def get_scanner_methods(scanner):
        """function, used to generate shell subcommands for run command"""
        methods = dict()
        for func in dir(scanner):
            if callable(getattr(scanner, func)) and re.match(
                    r'^_\d{3}_\w*$', func):
                methods[func[1:]] = getattr(scanner, func).__doc__
        return methods

    @property
    def get_toolbar(self):
        """function to display the bottom toolbar"""
        t_left = f'workspace: {self.config.start_config["MAIN"]["workspase"]} | ' \
                 f'host: {self.db.get_count_host} | ' \
                 f'socket: {self.db.get_count_socket}'
        t_right = f'{datetime.datetime.now().strftime("%H:%M:%S")} bat: {get_battery()}'

        rows, columns = os.popen('stty size', 'r').read().split()

        toolbar = t_left + ' ' * (int(columns) - len(t_left) -
                                  len(t_right)) + t_right

        return toolbar

    async def start(self):
        """main function starting the interface loop task"""
        os.system('clear')
        print_formatted_text(self.logo, style=self.style)

        # Create Prompt.
        while True:
            try:
                session = PromptSession(message=self.prompt_str,
                                        completer=self.completer,
                                        lexer=self.lexer,
                                        style=self.style,
                                        history=self.history,
                                        enable_history_search=True,
                                        auto_suggest=AutoSuggestFromHistory(),
                                        bottom_toolbar=self.get_toolbar,
                                        wrap_lines=False)

                result = await session.prompt(async_=True)

                self.log.debug(f'input: {result}')

                if not result:
                    continue

                elif result == 'clear':
                    clear()
                    continue

                elif result.strip().startswith('!P'):
                    try:
                        eval(result.strip().replace('!P', ''))
                    except Exception as e:
                        self.log.warning(e)
                        continue

                else:
                    m = self.grammar.match(result)
                    if m:
                        m_vars = m.variables()
                    else:
                        self.log.info('Invalid match')
                        continue

                    if m_vars.get('command') \
                            and m_vars.get('command') in list(self.commands.keys()) \
                            and m_vars.get('command') in list(self.c_function.keys()):

                        cm = m_vars.get('command')
                        cur_function = self.c_function[cm]

                        if len(self.commands.get(cm)) == 0:
                            cur_function()
                        else:
                            if m_vars.get('operator') and m_vars.get(
                                    'operator') in list(self.commands.get(cm)):
                                op = m_vars.get('operator')
                            else:
                                op = ''

                            if m_vars.get('parameter'):
                                pr = m_vars.get('parameter')
                            else:
                                pr = ''

                            cur_function(operator=op, parameter=pr)

                    else:
                        self.log.info('invalid command')
                        continue
            except (EOFError, KeyboardInterrupt):
                return

    def __call__(self):
        """function startin main asyncio loop"""
        async def sig_exit():
            self.log.info('Why are you so?')

        loop = asyncio.get_event_loop()

        for sig_name in ('SIGINT', 'SIGTERM'):
            loop.add_signal_handler(getattr(signal, sig_name),
                                    lambda: asyncio.ensure_future(sig_exit()))
        use_asyncio_event_loop()
        shell_task = asyncio.gather(self.start())
        with patch_stdout():
            loop.run_until_complete(shell_task)
            for task in asyncio.Task.all_tasks(loop=loop):
                if task is not asyncio.Task.current_task():
                    task.cancel()
Пример #4
0
class Mscanner:
    """masscan port scanner module class"""

    def __init__(self, prog_path, scans_path, db):
        """
        Initialization masscan scanner class object
        :param prog_path: path to the program
        :param scans_path: gummy_scan directory
        """
        self.log = Log(name='mscan')
        self.db = db

        self._prog_path = prog_path
        self._scans_path = scans_path

        self.scan_name = None
        self.target = None
        self.target_exclude = None
        self.target_exclude_file = None
        self.port = None
        self.udp_port = None
        self.top_ports = None
        self.rate = None

        self._ob_last_name = ''
        self._ox_last_name = ''
        self._conf_last_name = ''
        self._hosts_file_last_name = ''

        self.ob_last_path = ''
        self.ox_last_path = ''
        self.conf_last_path = ''
        self.hosts_file_last_path = ''

        self._args = []
        self.counter = 0

        self.version = ''
        self.host = {}

        self._check_prog()

    def _check_prog(self):
        """checking the correctness of the path to the program"""
        m_reg = re.compile(r'(?:Masscan version) (?P<ver>[\d.]*) (?:[\s\S]+)$')
        try:
            procc = subprocess.Popen([self._prog_path, '-V'], bufsize=10000, stdout=subprocess.PIPE)
            mach = m_reg.search(bytes.decode(procc.communicate()[0]))
            self.version = mach.group('ver')
            self.log.info(f'Use: {self._prog_path} (Version {self.version})')
        except Exception:
            self.log.warning('Masscan was not found')
            raise Exception()

    async def __call__(self, **kwargs):
        """
        start gummy_scan and save result in binary.
        :param kwargs:
        scan_name = first
        counter = 1
        target = 10.10.1.0/16
        includefile = <filename>
        target_exclude = 10.10.1.0/24, 10.10.2.0/24
        port = '443' or '80,443' or '22-25'
        udp_port = '443' or '80,443' or '22-25'
        top_ports = 100
        rate = 25000
        """
        self.scan_name = kwargs.get('scan_name')
        self.target = kwargs.get('target')
        self.includefile = kwargs.get('includefile')
        self.target_exclude = kwargs.get('target_exclude')
        self.hosts_file_last_path = kwargs.get('hosts_file_last_path')
        self.port = kwargs.get('port')
        self.udp_port = kwargs.get('udp_port')
        self.top_ports = kwargs.get('top_ports')
        self.rate = kwargs.get('rate')

        # parse start args
        if kwargs.get('counter') and kwargs.get('counter') is not None:
            self.counter = kwargs.get('counter')
        if self.scan_name and self.scan_name is not None:
            num = str(self.counter).zfill(3)
            if self.target:
                targ = self.target.replace('.', '-').replace('/', '#')
                targ = targ[:18] + '...' if len(targ) > 17 else targ
            else:
                targ = ''
            self._ob_last_name = f'{num}-m-{self.scan_name}-[{targ}].masscan'
            self._ox_last_name = f'{num}-m-{self.scan_name}-[{targ}].xml'
            self._conf_last_name = f'{num}-m-{self.scan_name}-[{targ}].conf'
            self._hosts_file_last_name = f'{num}-m-{self.scan_name}-[{targ}].host'

            self.ob_last_path = '/'.join((self._scans_path, self._ob_last_name))
            self.ox_last_path = '/'.join((self._scans_path, self._ox_last_name))
            self.conf_last_path = '/'.join((self._scans_path, self._conf_last_name))
            self.hosts_file_last_path = '/'.join((self._scans_path, self._hosts_file_last_name))
        else:
            self.log.warning('Missing required parameter: scan_name')
            return
        # generate masscan arg
        self._gen_args()

        self.counter += 1

        await self._run_scan()

        await self._convert_masscan_to_xml()

    def _gen_args(self):
        """generating arguments to run gummy_scan"""
        # clear list
        self._args.clear()

        # prog_path
        self._args.append(self._prog_path)

        # target
        if self.target:
            self._args.append('--range')
            self._args.append(self.target)
            self.log.debug(f'Set: {"target":10} Value: {self.target}')
        elif self.includefile:
            self._args.append('--includefile')
            self._args.append(self.includefile)
            self.log.debug(f'Set: {"includefile":10} Value: {self.includefile}')
        else:
            self.log.warning('Missing required parameter: target')
            return

        # target_exclude
        if self.target_exclude:
            self._args.append('--exclude')
            self._args.append(self.target_exclude)
            self.log.debug(f'Set: {"target_exclude":10} Value: {self.target_exclude}')

        # target_exclude_file
        if self.target_exclude_file:
            self._args.append('--excludefile')
            self._args.append(self.target_exclude)
            self.log.debug(f'Set: {"target_exclude_file":10} Value: {self.target_exclude_file}')

        # port or top-ports
        if self.port or self.udp_port:
            if self.port:
                self._args.append('--ports')
                self._args.append(self.port)
                self.log.debug(f'Set: {"port":10} Value: {self.port}')
            if self.udp_port:
                self._args.append('--udp-ports')
                self._args.append(self.udp_port)
                self.log.debug(f'Set: {"udp-ports":10} Value: {self.udp_port}')
        elif self.top_ports:
            self._args.append('--top-ports')
            self._args.append(self.top_ports)
            self.log.debug(f'Set: {"top_ports":10} Value: {self.top_ports}')
        else:
            self.log.warning('Missing required parameter: port or top-ports or udp-ports')
            return

        # output
        self._args.append('-oB')
        self._args.append(self.ob_last_path)
        self.log.debug(f'Set: {"output":10} Value: {self.ob_last_path}')

        # rate
        if self.rate:
            self._args.append('--rate')
            self._args.append(self.rate)
            self.log.debug(f'Set: {"rate":10} Value: {self.rate}')
        else:
            self.log.warning('Argument "rate" not set base value is used')

        # static args
        self._args.append('--wait')
        self._args.append('1')
        self._args.append('--interactive')

    async def _read_stream(self, stream):
        """asynchronous output processing"""
        res_reg_rem = re.compile(r'(?:rate:\s*)'
                                 r'(?P<Rate>[\d.]+)'
                                 r'(?:[-,\w]+\s+)'
                                 r'(?P<Persent>[\d.]*)'
                                 r'(?:%\s*done,\s*)'
                                 r'(?P<Time>[\d:]*)'
                                 r'(?:\s*remaining,\s*found=)'
                                 r'(?P<Found>[\d]*)')

        res_reg_dis = re.compile(r'(?:Discovered open port )'
                                 r'(?P<Port>\d+)'
                                 r'(?:/)'
                                 r'(?P<Protocol>\w+)'
                                 r'(?: on )'
                                 r'(?P<IP>[\d.]+)')

        old_line = ''
        persent_old, found_old = 0, 0
        mach_udp = 0
        temp_soc_stor = list()
        while True:
            line = await stream.read(n=1000)
            if line:
                line = line.decode(locale.getpreferredencoding(False))
                line = line.replace('\n', '')
                line = line.replace('\r', '')
                if line != old_line:
                    mach_rem = res_reg_rem.search(line)
                    mach_dis = res_reg_dis.search(line)
                    if mach_rem:
                        persent_new = mach_rem.group("Persent")
                        found_new = mach_rem.group("Found")
                        if found_new != found_old or float(persent_new) >= float(persent_old) + 5:
                            persent_old, found_old = persent_new, found_new
                            self.log.info(f'[{persent_new}%] '
                                          f'Time: {mach_rem.group("Time")} '
                                          f'Found: {int(found_new) + mach_udp}')
                    if mach_dis:
                        mach_soc = [{'addr': mach_dis.group('IP'),
                                     'ports': [{'protocol': mach_dis.group('Protocol'),
                                                'portid': mach_dis.group('Port'),
                                                'state': 'open'}]}]

                        if mach_soc not in temp_soc_stor:  # check for duplicate records:
                            temp_soc_stor.append(mach_soc)
                            if mach_dis.group('Protocol') == 'udp':
                                mach_udp += 1

                            self.db + mach_soc

                old_line = line
            else:
                break

    async def _run_scan(self):
        """run a gummy_scan using arguments"""
        self.log.debug(f'Write the command to a file {self.conf_last_path}')
        with open(self.conf_last_path, "w") as text_file:
            text_file.write(' '.join(self._args))
        self.log.info('Scan start')
        self.log.debug(f'run: {" ".join(self._args)}')

        proc = await asyncio.create_subprocess_exec(*self._args,
                                                    stdout=asyncio.subprocess.PIPE,
                                                    stderr=asyncio.subprocess.STDOUT)

        await asyncio.wait([self._read_stream(proc.stdout)])
        await proc.wait()

        self.log.info('Scan complete')

    async def _convert_masscan_to_xml(self):
        """convert masscan binary to xml format"""

        if os.stat(self.ob_last_path).st_size == 0:
            self.log.warning('The file is empty')
        else:
            self.log.debug(f'Сonvert {self.ob_last_path} to {self.ox_last_path}')

            args = [self._prog_path] + \
                   ['--readscan', self.ob_last_path] + \
                   ['-oX', self.ox_last_path]

            proc = await asyncio.create_subprocess_exec(*args,
                                                        stdout=asyncio.subprocess.PIPE,
                                                        stderr=asyncio.subprocess.STDOUT)

            stdout, stderr = await proc.communicate()
            self.log.info(stdout.decode().strip())
Пример #5
0
class Nscanner:
    """nmap scanner module class"""

    # TODO the process does not end at task.cancel()
    def __init__(self, prog_path, db, scans_path):
        """
        initialization nmap scanner class object
        :param prog_path: path to the program
        :param scans_path: gummy_scan directory
        """
        self.log = Log(name='nscan')
        self.db = db
        self.parser = Parser()

        self._prog_path = prog_path
        self._scans_path = scans_path
        self.scan_name = None
        self.target = None
        self.port = None
        self.udp_port = None
        self.scan_type = None
        self.version = None
        self._ox_last_name = None
        self.ox_last_path = None
        self._t_missing = 'Missing required parameter: {}'
        self._args = list()
        self.counter = 0
        self._check_prog()

        self._args_basic = [
            '-sV', '-Pn', '--disable-arp-ping', '-T4', '-O', '--version-light',
            '--stats-every', '1s'
        ]
        self._args_arp = ['-PR', '-sn', '--stats-every', '1s']
        self._args_dns = ['-sL']

    def _check_prog(self):
        """checking the correctness of the path to the program"""
        m_reg = re.compile(r'(?:Nmap version) (?P<ver>[\d.]*) (?:[\s\S]+)$')
        try:
            procc = subprocess.Popen([self._prog_path, '-V'],
                                     bufsize=10000,
                                     stdout=subprocess.PIPE)
            mach = m_reg.search(bytes.decode(procc.communicate()[0]))
            self.version = mach.group('ver')
            self.log.debug(f'Use: {self._prog_path} (Version {self.version})')
        except Exception:
            self.log.warning('Nmap was not found')
            raise Exception()

    async def __call__(self, **kwargs):
        """
        Start gummy_scan and save result in XML.
        001-basic-n-[192-168-1-50#24]-basic.xml
        :param kwargs:
        scan_name = first
        counter = 1
        target = 10.10.1.0/16
        port = '443' or '80,443' or '22-25'
        udp_port = '443' or '80,443' or '22-25'
        scan_type = 'fast' or 'basic' or 'full'
        :return:
        """
        self.scan_name = kwargs.get('scan_name')
        self.target = kwargs.get('target')
        self.port = kwargs.get('port')
        self.udp_port = kwargs.get('udp_port')
        self.scan_type = kwargs.get('scan_type')

        if kwargs.get('counter') and kwargs.get('counter') is not None:
            self.counter = kwargs.get('counter')

        targ = self.target.replace('.', '-').replace('/', '#')
        targ = targ[:18] + '...' if len(targ) > 17 else targ
        self._ox_last_name = f'{str(self.counter).zfill(3)}-n-{self.scan_name}-[{targ}]-{self.scan_type}.xml'

        self.ox_last_path = '/'.join((self._scans_path, self._ox_last_name))

        if self._gen_args():
            await self._run_scan()

            self.parser(self.ox_last_path)
            # noinspection PyStatementEffect
            self.db + self.parser.result

    def _gen_args(self):
        """generating arguments to run gummy_scan"""
        # clear list
        self._args.clear()

        # required parameters:
        # prog_path
        self._args.append(self._prog_path)

        # target
        if self.target:
            self._args.append(self.target)
            self.log.debug(f'Set: {"target":10} Value: {self.target}')
        else:
            self.log.warning(self._t_missing.format('target'))
            return False

        # output
        self._args.append('-oX')
        self._args.append(self.ox_last_path)
        self.log.debug(f'Set: {"output":10} Value: {self.ox_last_path}')

        # optional parameters
        # port
        temp_ports_arg = []
        temp_port = []
        if self.port or self.udp_port:
            if self.port:
                temp_ports_arg.append('-sS')
                temp_port.append('T:' + self.port)

            if self.udp_port:
                temp_ports_arg.append('-sU')
                temp_port.append('U:' + self.udp_port)

            temp_ports_arg.append('-p')
            temp_ports_arg.append(','.join(temp_port))

            self.log.debug(f'Set: {"port":10} Value: {",".join(temp_port)}')

        if self.scan_type == 'basic':
            self._args += temp_ports_arg
            self._args += self._args_basic

        elif self.scan_type == 'arp':
            self._args += self._args_arp

        elif self.scan_type == 'dns':
            self._args += self._args_dns

        return True

    async def _read_stream(self, stream):
        """asynchronous output processing"""
        full_body = ''
        last_print_line = 0

        exclude_lines = [
            r'^WARNING: Running Nmap setuid, as you are doing, is a major security risk.$',
            r'^WARNING: Running Nmap setgid, as you are doing, is a major security risk.$',
            r'^Starting Nmap .*$', r'^$', r'^Host is up.$',
            r'^Nmap scan report for [\d.]*$'
        ]
        while True:
            line = await stream.read(n=100)
            if line:
                line = line.decode(locale.getpreferredencoding(False))
                full_body += line
                full_body_list = full_body.split('\n')
                total_line = len(full_body_list) - 1
                if total_line > last_print_line:
                    for line in full_body_list[last_print_line:total_line]:
                        if any(
                                re.search(regex, line)
                                for regex in exclude_lines):
                            self.log.debug(line)
                        else:
                            self.log.info(line)
                    last_print_line = total_line
            else:
                break

    async def _run_scan(self):
        """run a gummy_scan using arguments"""

        self.log.info('Scan start')
        self.log.debug(f'run: {" ".join(self._args)}')

        proc = await asyncio.create_subprocess_exec(
            *self._args,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT)

        await asyncio.wait([self._read_stream(proc.stdout)])
        await proc.wait()

        self.counter += 1
        self.log.info('Scan complete')
Пример #6
0
class Scanner:
    """This class describes gummy_scan profiles, links gummy_scan modules and auxiliary modules."""
    def __init__(self, db=None):
        """class object initialization"""
        # basic param:
        self.db = db
        self.log = Log(name='gummy')
        self.port_master = PortMaster()
        self.counter = 0

        # complex gummy_scan param:
        self.complex_n_scan = None
        self.complex_m_scan = None
        self.complex_pars = None
        self.complex_res = None
        self.complex_hosts_file_last_path = None
        self.complex_step1 = False
        self.complex_step2 = False
        self.complex_step3 = False
        self.complex_step4 = False
        self.complex_confirmation_time = datetime.datetime.now()

        # config param:
        self.config = None
        self.masscan_path = None
        self.nmap_path = None
        self.workspace_path = None
        self.target = None
        self.target_exclude = None
        self.port = None
        self.top_ports = None
        self.rate = None
        self.scan_type = None

        # port ranges param:
        self.tcp_stage_1 = self.port_master(start=1, end=1000, protocol='tcp')
        self.tcp_stage_2 = self.port_master(start=1001,
                                            end=65535,
                                            protocol='tcp')
        self.udp_stage_1 = self.port_master(start=1, end=1000, protocol='udp')
        self.udp_stage_2 = self.port_master(start=1001,
                                            end=4000,
                                            protocol='udp')

    def create_hosts_file(self, file, hosts):
        """function that creates a file with a list of hosts"""
        try:
            with open(file, "w") as host_file:
                for host in hosts:
                    host_file.write(host + '\n')
        except IOError:
            self.log.warning('failed to create file!')

    def sync(self, config):
        """updates settings from configuration class instance"""
        self.config = config
        self.masscan_path = config['MAIN'].get('masscan_path')
        self.nmap_path = config['MAIN'].get('nmap_path')
        self.workspace_path = config['MAIN'].get('workspace_path')
        self.target = config['MASSCAN'].get('target')
        self.target_exclude = config['MASSCAN'].get('target_exclude')
        self.port = config['MASSCAN'].get('port')
        self.top_ports = config['MASSCAN'].get('top_ports')
        self.rate = config['MASSCAN'].get('rate')
        self.scan_type = config['NMAP'].get('scan_type')

    async def __complex(self, stage):
        """
        updates settings from configuration class instance
        takes an array with the necessary scanning steps
        used own variables of self.complex_... type
        :param stage: list[int]
        :return:
        """
        if 1 in stage:
            if all([i is None for i in [self.complex_m_scan, self.complex_n_scan, self.complex_pars]]) \
                    or datetime.datetime.now() < (self.complex_confirmation_time + datetime.timedelta(seconds=10)):
                self.complex_step2, self.complex_step3, self.complex_step4 = False, False, False

                self.complex_n_scan = Nscanner(prog_path=self.nmap_path,
                                               scans_path=self.workspace_path,
                                               db=self.db)

                self.complex_m_scan = Mscanner(prog_path=self.masscan_path,
                                               scans_path=self.workspace_path,
                                               db=self.db)
                self.complex_pars = Parser()
                self.counter += 1
                self.log.info(f'{" STEP 1 ":#^40}')

                await self.complex_n_scan(scan_name='arp',
                                          counter=self.counter,
                                          target=self.target,
                                          scan_type='arp')
                self.complex_pars(file=self.complex_n_scan.ox_last_path)
                arp_host = self.complex_pars.hosts
                self.counter += 1
                await self.complex_m_scan(scan_name='stage_1',
                                          counter=self.counter,
                                          target=self.target,
                                          target_exclude=self.target_exclude,
                                          port=self.tcp_stage_1,
                                          udp_port=self.udp_stage_1,
                                          rate=self.rate)
                self.complex_pars(file=self.complex_m_scan.ox_last_path)
                self.create_hosts_file(
                    hosts=set(arp_host + self.complex_pars.hosts),
                    file=self.complex_m_scan.hosts_file_last_path)
                self.complex_hosts_file_last_path = self.complex_m_scan.hosts_file_last_path
                self.complex_res = self.complex_pars.result
                self.db + self.complex_pars.result
                self.complex_step1 = True

            else:
                self.log.info(
                    'А complex gummy_scan has already been started, if you want to override it - '
                    'repeat start command for the next 10 seconds')
                self.complex_confirmation_time = datetime.datetime.now()
                return
        if 2 in stage:
            if self.complex_step1 and len(self.complex_pars.result) != 0:
                self.counter += 1
                self.log.info(f'{" STEP 2 ":#^40}')
                await self.complex_m_scan(
                    scan_name='stage_2',
                    counter=self.counter,
                    # target=','.join(self.complex_pars.hosts),
                    includefile=self.complex_hosts_file_last_path,
                    target_exclude=self.target_exclude,
                    port=self.tcp_stage_2,
                    udp_port=self.udp_stage_2,
                    rate=self.rate)

                self.complex_pars(file=self.complex_m_scan.ox_last_path)
                self.create_hosts_file(
                    hosts=self.complex_pars.hosts,
                    file=self.complex_m_scan.hosts_file_last_path)
                self.complex_res = self.db.merge(self.complex_res,
                                                 self.complex_pars.result)
                self.db + self.complex_pars.result
                self.complex_step2 = True
            else:
                self.log.info('There are no results of the previous stage')
        if 3 in stage:
            if self.complex_step2:
                self.log.info(f'{" STEP 3 ":#^40}')
                self.complex_n_scan = Nscanner(prog_path=self.nmap_path,
                                               scans_path=self.workspace_path,
                                               db=self.db)

                for host in self.complex_res:
                    self.counter += 1
                    tcp = list()
                    udp = list()
                    for port in host['ports']:
                        if port['state'] == 'open':
                            if port['protocol'] == 'tcp':
                                tcp.append(port['portid'])
                            elif port['protocol'] == 'udp':
                                udp.append(port['portid'])

                    self.log.info(
                        f'{host["addr"]} tcp:{",".join(tcp)} udp:{",".join(udp)}'
                    )
                    await self.complex_n_scan(scan_name='basic',
                                              counter=self.counter,
                                              target=host['addr'],
                                              port=','.join(tcp),
                                              udp_port=','.join(udp),
                                              scan_type='basic')
                self.complex_step3 = True
            else:
                self.log.info('There are no results of the previous stage')
        if 4 in stage:
            if self.complex_step3:
                self.counter += 1
                self.log.info(f'{" STEP 4 ":#^40}')
                await self.complex_m_scan(
                    scan_name='swamp',
                    counter=self.counter,
                    target=self.target,
                    target_exclude=self.complex_hosts_file_last_path,
                    port=self.tcp_stage_2,
                    udp_port=self.udp_stage_2,
                    rate=self.rate)
                self.complex_pars(file=self.complex_m_scan.ox_last_path)
                self.create_hosts_file(
                    hosts=self.complex_pars.hosts,
                    file=self.complex_m_scan.hosts_file_last_path)
                self.db + self.complex_pars.result
                self.log.info('I found something in the swamp !!!')
                self.log.info(self.complex_pars.hosts)
                self.complex_step4 = True
                self.log.info(f'{" END ":#^40}')
            else:
                self.log.info('There are no results of the previous stage')

    # next following are the functions displayed in the user interface
    # you must use the functions starting with _iii_...
    # and add a short description

    def _101_complex_1(self):
        """M Scan the top 1000 TCP and top 1000 UDP ports of the current range"""
        asyncio.gather(self.__complex(stage=[1]))

    def _102_complex_2(self):
        """M Scan the bottom 64553 TCP and next 3000 UDP ports of the detected hosts"""
        asyncio.gather(self.__complex(stage=[2]))

    def _103_complex_3(self):
        """N Scan the detected hosts (found ports)"""
        asyncio.gather(self.__complex(stage=[3]))

    def _104_complex_4(self):
        """M Scan the remaining swamp"""
        asyncio.gather(self.__complex(stage=[4]))

    def _111_complex_1_2(self):
        """Sequential start of steps 1-2 complex gummy_scan"""
        asyncio.gather(self.__complex(stage=[1, 2]))

    def _112_complex_1_3(self):
        """Sequential start of steps 1-3 complex gummy_scan"""
        asyncio.gather(self.__complex(stage=[1, 2, 3]))

    def _113_complex_1_4(self):
        """Sequential start of steps 1-4 complex gummy_scan"""
        asyncio.gather(self.__complex(stage=[1, 2, 3, 4]))

    def _001_masscan(self):
        """Run Masscan manually"""
        self.counter += 1

        m_scan = Mscanner(prog_path=self.masscan_path,
                          scans_path=self.workspace_path,
                          db=self.db)

        asyncio.gather(
            m_scan(scan_name='basic',
                   counter=self.counter,
                   target=self.target,
                   target_exclude=self.target_exclude,
                   port=self.port,
                   top_ports=self.top_ports,
                   rate=self.rate))

    def _002_nmap(self):
        """Run Nmap manually"""
        self.counter += 1
        n_scan = Nscanner(prog_path=self.nmap_path,
                          scans_path=self.workspace_path,
                          db=self.db)

        asyncio.gather(
            n_scan(scan_name=self.scan_type,
                   counter=self.counter,
                   target=self.target,
                   port=self.port,
                   scan_type=self.scan_type))

    def _201_arp_discovery(self):
        """Discovering hosts with ARP ping scans (-PR)"""
        self.counter += 1
        n_scan = Nscanner(prog_path=self.nmap_path,
                          scans_path=self.workspace_path,
                          db=self.db)

        asyncio.gather(
            n_scan(scan_name='arp',
                   counter=self.counter,
                   target=self.target,
                   scan_type='arp'))

    def _202_dns_discovery(self):
        """Reverse DNS resolution (-sL)"""
        self.counter += 1
        n_scan = Nscanner(prog_path=self.nmap_path,
                          scans_path=self.workspace_path,
                          db=self.db)

        asyncio.gather(
            n_scan(scan_name='dns',
                   counter=self.counter,
                   target=self.target,
                   scan_type='dns'))