Ejemplo n.º 1
0
def main(args: Optional[List[str]]=None) -> int:
    """Main entry point for `bscan-shells`'s command-line interface."""
    try:
        opts = get_parsed_args(args)
        target = opts.target
        if not is_valid_ip_host_addr(target) and not is_valid_hostname(target):
            raise BscanConfigError('Invalid target specified: ' + target)

        try:
            port = (80 if opts.port is None else int(opts.port))
        except ValueError:
            raise BscanConfigError('Invalid port specified: ' + opts.port)

        if opts.url_encode:
            attr = 'url_encoded_cmd'
        else:
            attr = 'cmd'

        _cmd_iter = sorted(
            reverse_shell_commands(target, port), key=lambda c: c.name)
        for rev_shell_cmd in _cmd_iter:
            print(rev_shell_cmd.name)
            print(getattr(rev_shell_cmd, attr))
            print()

        return 0
    except BscanConfigError as e:
        print('Configuration error:', e.message, file=sys.stderr)
        return 1
    except Exception as e:
        print('Received unexpected exception; re-raising it!', file=sys.stderr)
        raise e
Ejemplo n.º 2
0
def load_default_config_file(filename: str) -> str:
    """Packaged-friendly method to load contents of a default config file."""
    try:
        raw_contents = resource_string(__name__, 'configuration/' + filename)
    except FileNotFoundError:
        raise BscanConfigError('Unable to find default configuration file `' +
                               filename + '`')
    return raw_contents.decode('utf-8')
Ejemplo n.º 3
0
def load_default_config_file(filename: str) -> str:
    """Packaged-friendly method to load contents of a default config file."""
    try:
        pyinst_basedir = getattr(sys, '_MEIPASS', None)
        if pyinst_basedir is not None:
            # load configuration from PyInstaller bundle
            filepath = os.path.join(pyinst_basedir, 'configuration', filename)
            with open(filepath, 'r') as f:
                raw_contents = f.read()
        else:
            # load configuration from either Python wheel or the filesystem
            raw_contents = resource_string(__name__, 'configuration/' +
                                           filename).decode('utf-8')
    except FileNotFoundError:
        raise BscanConfigError('Unable to find default configuration file `' +
                               filename + '`')

    return raw_contents
Ejemplo n.º 4
0
async def main(args: Optional[List[str]] = None) -> int:
    """Main entry point for `bscan`'s command-line interface.

    Args:
        args: Custom arguments to override ``sys.argv``.

    Returns:
        The exit code of the program.

    """
    try:
        init_colorama()

        if not good_py_version():
            print_w_d1('Running with Python version ', py_version_str(),
                       'but this program is only tested with Python 3.6')

        opts = get_parsed_args(args)
        print_i_d1('Initializing configuration from command-line arguments')
        mc = opts.max_concurrency
        try:
            mc = (20 if mc is None else int(mc))
            if mc < 1:
                raise ValueError
        except ValueError:
            raise BscanConfigError(
                'Invalid `--max-concurrency` positive integer value '
                'received: ' + str(mc))

        async with Sublemon(max_concurrency=mc) as subl:
            await write_db_value('sublemon', subl)
            await init_config(opts)

            print_color_info()

            if not opts.targets:
                print_e_d1('No targets specified; use `--help` to figure '
                           'out what you\'re doing')
                return 1

            # TODO: create a full list of targets from network address and
            #       --ping-sweep filtering
            targets = []
            _target_set: Set[str] = set()
            for candidate in opts.targets:
                if candidate in _target_set:
                    print_w_d1(
                        'Target ', candidate, ' has already been added as a '
                        'target; skipping another attempted addition')
                    continue
                elif is_valid_ip_host_addr(candidate):
                    pass
                elif is_valid_hostname(candidate):
                    pass
                elif is_valid_ip_net_addr(candidate):
                    print_w_d1(
                        'Network scanning not yet supported; '
                        'skipping network: ', candidate)
                    continue
                else:
                    print_e_d1('Unable to parse target ', candidate,
                               ', skipping it')
                    continue

                try:
                    create_dir_skeleton(candidate)
                except BscanForceSkipTarget as e:
                    print_e_d1(e.message)
                    print_e_d1(candidate, ': skipping this target')
                    continue

                targets.append(candidate)
                _target_set.add(candidate)

            if not targets:
                print_e_d1('No valid targets specified')
                return 1

            print_i_d1('Kicking off scans of ', len(targets), ' targets')
            tasks = [scan_target(target) for target in targets]
            if get_db_value('status-interval') > 0:
                tasks.append(status_update_poller())
            await asyncio.gather(*tasks)

            print_i_d1('Completed execution')
            return 0
    except BscanConfigError as e:
        print_e_d1('Configuration error: ', e.message)
        return 1
    except BscanForceSilentExit as e:
        return 1
    except BscanInternalError as e:
        print_e_d1('Internal error: ', e.message)
        return 1
    except BscanSubprocessError as e:
        print_e_d1('Error handling subprocess: ', e.message)
        return 1
    except BscanError as e:
        print_e_d1('This should not be reached!')
        return 1
    except Exception as e:
        print_e_d1('Received unexpected exception; re-raising it.',
                   file=sys.stderr)
        raise e
Ejemplo n.º 5
0
async def init_config(ns: Namespace) -> None:
    """Init configuration from default files and command-line arguments."""
    async with lock:
        # track targets being actively scanned
        db['active-targets'] = set()

        # --brute-pass-list
        if ns.brute_pass_list is None:
            db['brute-pass-list'] = '/usr/share/wordlists/fasttrack.txt'
        else:
            db['brute-pass-list'] = ns.brute_pass_list
        if not ns.no_file_check and not file_exists(db['brute-pass-list']):
            raise BscanConfigError('`--brute-pass-list` file ' +
                                   db['brute-pass-list'] + ' does not exist')

        # --brute-user-list
        if ns.brute_user_list is None:
            db['brute-user-list'] = (
                '/usr/share/wordlists/metasploit/namelist.txt')
        else:
            db['brute-user-list'] = ns.brute_user_list
        if not ns.no_file_check and not file_exists(db['brute-user-list']):
            raise BscanConfigError('`--brute-user-list` file ' +
                                   db['brute-user-list'] + ' does not exist')

        # --cmd-print-width
        try:
            cmd_print_width = (80 if ns.cmd_print_width is None else int(
                ns.cmd_print_width))
            if cmd_print_width < 5:
                raise ValueError
        except ValueError:
            raise BscanConfigError(
                'Invalid `--cmd-print-width` value specified; must be an '
                'integer greater than or equal to 5')
        db['cmd-print-width'] = cmd_print_width

        # --output-dir
        if ns.output_dir is None:
            db['output-dir'] = os.getcwd()
        else:
            db['output-dir'] = ns.output_dir
        if not dir_exists(db['output-dir']):
            raise BscanConfigError('`--output-dir` directory ' +
                                   db['output-dir'] + ' does not exist')

        # --patterns; also loads from `configuration/patterns.txt`
        patterns = load_config_file('patterns.txt', ns.config_dir).splitlines()
        if ns.patterns is not None:
            if not ns.patterns:
                raise BscanConfigError(
                    '`--patterns` requires at least one regex pattern')
            else:
                patterns.extend(ns.patterns)
        db['patterns'] = re.compile('|'.join(patterns))

        # --no-program-check
        if not ns.no_program_check:
            not_found_progs = []
            progs = load_config_file('required-programs.txt',
                                     ns.config_dir).splitlines()
            for prog in progs:
                if shutil.which(prog) is None:
                    not_found_progs.append(prog)

            if not_found_progs:
                raise BscanConfigError('required programs ' +
                                       ', '.join(not_found_progs) +
                                       ' could not be found on this system')

        # --no-service-scans
        db['no-service-scans'] = ns.no_service_scans

        # load service information from `configuration/service-scans.toml`
        db['services'] = toml.loads(
            load_config_file('service-scans.toml', ns.config_dir))

        # load quick scan method configuration
        # derived from `--qs-method` + `configuration/port-scans.toml`
        port_scan_config = toml.loads(
            load_config_file('port-scans.toml', ns.config_dir))
        qs_config = port_scan_config['quick']
        qs_method_name = (ns.qs_method if ns.qs_method is not None else
                          qs_config['default'])
        if qs_method_name not in qs_config or qs_method_name == 'default':
            raise BscanConfigError('Invalid `--qs-method` specified: ' +
                                   str(qs_method_name))
        qs_attrs = qs_config[qs_method_name]
        db['quick-scan'] = PortScanConfig(qs_method_name,
                                          re.compile(qs_attrs['pattern']),
                                          qs_attrs['scan'])

        # load thorough scan method configuration
        # derived from `--ts-method` + `configuration/port-scans.toml`
        ts_config = port_scan_config['thorough']
        ts_method_name = (ns.ts_method if ns.ts_method is not None else
                          ts_config['default'])
        if ts_method_name not in ts_config or ts_method_name == 'default':
            raise BscanConfigError('Invalid `--ts-method` specified: ' +
                                   str(ts_method_name))
        ts_attrs = ts_config[ts_method_name]
        db['thorough-scan'] = PortScanConfig(ts_method_name,
                                             re.compile(ts_attrs['pattern']),
                                             ts_attrs['scan'])

        # load udp scan method configuration
        # derived from `--udp-method` + `configuration/port-scans.toml`
        udp_config = port_scan_config['udp']
        udp_method_name = (ns.udp_method if ns.udp_method is not None else
                           udp_config['default'])
        if udp_method_name not in udp_config or udp_method_name == 'default':
            raise BscanConfigError('Invalid `--udp-method` specified: ' +
                                   str(udp_method_name))
        udp_attrs = udp_config[udp_method_name]
        db['udp-scan'] = PortScanConfig(udp_method_name,
                                        re.compile(udp_attrs['pattern']),
                                        udp_attrs['scan'])

        # --status-interval
        try:
            db['status-interval'] = (30 if ns.status_interval is None else int(
                ns.status_interval))
        except ValueError:
            raise BscanConfigError(
                'Invalid `--status-interval` integer specified: ' +
                str(ns.status_interval))

        # runtime tracking of active subprocesses
        db['subprocesses'] = dict()

        # --web-word-list
        if ns.web_word_list is None:
            db['web-word-list'] = '/usr/share/dirb/wordlists/big.txt'
        else:
            db['web-word-list'] = ns.web_word_list
        if not ns.no_file_check and not file_exists(db['web-word-list']):
            raise BscanConfigError('`--web-word-list` file ' +
                                   db['web-word-list'] + ' does not exist')

        # --quick-only
        db['quick-only'] = ns.quick_only

        # --hard
        db['hard'] = ns.hard

        # --ping-sweep
        if ns.ping_sweep:
            raise BscanConfigError('`--ping-sweep` option not yet implemented')
        db['ping-sweep'] = ns.ping_sweep

        # --udp
        db['udp'] = ns.udp

        # --verbose-status
        db['verbose-status'] = ns.verbose_status