Ejemplo n.º 1
0
def load_modules_from_dir(mod_dir, ignore=None):
    if ignore is None:
        ignore = {}
    for file in os.listdir(mod_dir):
        if file in ignore:
            continue
        path = mod_dir + "/" + file
        if os.path.isdir(path) and os.path.exists(path + "/module.py"):
            module = __import__(path.replace("/", ".") + ".module",
                                globals(),
                                locals(),
                                fromlist=["Module", "load_module"])
            do_load = True
            if hasattr(module, "load_module"):
                do_load = module.load_module
            if do_load and hasattr(module, "Module"):
                instance = module.Module()
                ModuleManager().add_module(instance)
                logger.log(2, "Loaded module \"%s\"" % instance.name)
Ejemplo n.º 2
0
    def __init__(self, networks: list, add_networks: list, omit_networks: list,
                 update_modules: bool, config_path: str, ports: list,
                 output_dir: str, user_results: dict, single_network: bool,
                 verbose: bool):
        """
        Create a Controller object.

        :param networks: A list of strings specifying the networks to analyze
        :param add_networks: A list of networks as strings to additionally analyze
        :param omit_networks: A list of networks as strings to omit from the analysis
        :param update_modules: Whether modules should be updated or initialized
        :param config_path: The path to a config file
        :param ports: A list of port expressions
        :param output_dir: A string specifying the output directory of the analysis
        :param user_results: A list of filenames whose files contain user provided results
        :param single_network: A boolean specifying whether all given networks are to be considered
                               hosts in one single network
        :param vebose: Specifying whether to provide verbose output or not
        """

        self.networks = networks if networks is not None else []
        self.networks += add_networks if add_networks is not None else []
        self.omit_networks = omit_networks

        # determine output directory
        if output_dir:
            self.output_dir = output_dir
        else:
            self.output_dir = "avain_output-" + util.get_current_timestamp()
        self.orig_out_dir = self.output_dir
        self.output_dir = os.path.abspath(self.output_dir)
        os.makedirs(self.output_dir, exist_ok=True)

        # check for user scan and analysis results
        self.user_results = {}

        if user_results:
            for rtype, filenames in user_results.items():
                if rtype not in self.user_results:
                    self.user_results[rtype] = []
                for filename in filenames:
                    self.user_results[rtype].append(
                        (filename, os.path.abspath(filename)))

        # store absolute config path
        if config_path:
            config_path = os.path.abspath(config_path)

        # change into AVAIN directory
        self.original_cwd = os.getcwd()
        core_dir = os.path.dirname(os.path.join(os.path.realpath(__file__)))
        avain_dir = os.path.abspath(os.path.join(core_dir, os.pardir))
        os.chdir(avain_dir)

        # parse default and user configs
        self.config = {}
        if os.path.isfile(DEFAULT_CONFIG_PATH):
            try:
                self.config = util.parse_config(DEFAULT_CONFIG_PATH,
                                                self.config)
            except Exception as excpt:
                print(util.MAGENTA +
                      ("Warning: Could not parse default config file. " +
                       "Proceeding without default config.\n") + util.SANE,
                      file=sys.stderr)
                util.print_exception_and_continue(excpt)
        elif not config_path:
            print(util.MAGENTA + "Warning: Could not find default config.\n" +
                  util.SANE,
                  file=sys.stderr)

        if config_path:
            try:
                self.config = util.parse_config(config_path, self.config)
            except Exception as excpt:
                print(util.MAGENTA +
                      ("Warning: Could not parse custom config file. " +
                       "Proceeding without custom config.\n") + util.SANE,
                      file=sys.stderr)
                util.print_exception_and_continue(excpt)

        # set remaining variables
        self.single_network = single_network
        self.verbose = verbose
        self.ports = ports
        self.update_modules = update_modules
        if (not self.update_modules) and self.config["core"][
                "automatic_module_updates"].lower() == "true":
            # retrieve last update timestamp based on last CPE dict download time
            last_update = util.get_creation_date(
                "modules/resources/official-cpe-dictionary_v2.2.xml")
            passed_time = datetime.datetime.now() - last_update
            update_interval = datetime.timedelta(
                minutes=int(self.config["core"]["module_update_interval"]))
            if passed_time > update_interval:
                util.printit(
                    "[INFO] Module data is out-of-date and will be updated\n",
                    color=util.MAGENTA)
                self.update_modules = True

        # setup module_manager
        self.module_manager = ModuleManager(self.networks, self.output_dir,
                                            self.omit_networks, self.ports,
                                            self.user_results, self.config,
                                            self.verbose)

        # setup logging
        self.setup_logging()
        self.logger.info("Starting the AVAIN program")
        self.logger.info("Executed call: avain %s", " ".join(sys.argv[1:]))
Ejemplo n.º 3
0
class Controller():
    def __init__(self, networks: list, add_networks: list, omit_networks: list,
                 update_modules: bool, config_path: str, ports: list,
                 output_dir: str, user_results: dict, single_network: bool,
                 verbose: bool):
        """
        Create a Controller object.

        :param networks: A list of strings specifying the networks to analyze
        :param add_networks: A list of networks as strings to additionally analyze
        :param omit_networks: A list of networks as strings to omit from the analysis
        :param update_modules: Whether modules should be updated or initialized
        :param config_path: The path to a config file
        :param ports: A list of port expressions
        :param output_dir: A string specifying the output directory of the analysis
        :param user_results: A list of filenames whose files contain user provided results
        :param single_network: A boolean specifying whether all given networks are to be considered
                               hosts in one single network
        :param vebose: Specifying whether to provide verbose output or not
        """

        self.networks = networks if networks is not None else []
        self.networks += add_networks if add_networks is not None else []
        self.omit_networks = omit_networks

        # determine output directory
        if output_dir:
            self.output_dir = output_dir
        else:
            self.output_dir = "avain_output-" + util.get_current_timestamp()
        self.orig_out_dir = self.output_dir
        self.output_dir = os.path.abspath(self.output_dir)
        os.makedirs(self.output_dir, exist_ok=True)

        # check for user scan and analysis results
        self.user_results = {}

        if user_results:
            for rtype, filenames in user_results.items():
                if rtype not in self.user_results:
                    self.user_results[rtype] = []
                for filename in filenames:
                    self.user_results[rtype].append(
                        (filename, os.path.abspath(filename)))

        # store absolute config path
        if config_path:
            config_path = os.path.abspath(config_path)

        # change into AVAIN directory
        self.original_cwd = os.getcwd()
        core_dir = os.path.dirname(os.path.join(os.path.realpath(__file__)))
        avain_dir = os.path.abspath(os.path.join(core_dir, os.pardir))
        os.chdir(avain_dir)

        # parse default and user configs
        self.config = {}
        if os.path.isfile(DEFAULT_CONFIG_PATH):
            try:
                self.config = util.parse_config(DEFAULT_CONFIG_PATH,
                                                self.config)
            except Exception as excpt:
                print(util.MAGENTA +
                      ("Warning: Could not parse default config file. " +
                       "Proceeding without default config.\n") + util.SANE,
                      file=sys.stderr)
                util.print_exception_and_continue(excpt)
        elif not config_path:
            print(util.MAGENTA + "Warning: Could not find default config.\n" +
                  util.SANE,
                  file=sys.stderr)

        if config_path:
            try:
                self.config = util.parse_config(config_path, self.config)
            except Exception as excpt:
                print(util.MAGENTA +
                      ("Warning: Could not parse custom config file. " +
                       "Proceeding without custom config.\n") + util.SANE,
                      file=sys.stderr)
                util.print_exception_and_continue(excpt)

        # set remaining variables
        self.single_network = single_network
        self.verbose = verbose
        self.ports = ports
        self.update_modules = update_modules
        if (not self.update_modules) and self.config["core"][
                "automatic_module_updates"].lower() == "true":
            # retrieve last update timestamp based on last CPE dict download time
            last_update = util.get_creation_date(
                "modules/resources/official-cpe-dictionary_v2.2.xml")
            passed_time = datetime.datetime.now() - last_update
            update_interval = datetime.timedelta(
                minutes=int(self.config["core"]["module_update_interval"]))
            if passed_time > update_interval:
                util.printit(
                    "[INFO] Module data is out-of-date and will be updated\n",
                    color=util.MAGENTA)
                self.update_modules = True

        # setup module_manager
        self.module_manager = ModuleManager(self.networks, self.output_dir,
                                            self.omit_networks, self.ports,
                                            self.user_results, self.config,
                                            self.verbose)

        # setup logging
        self.setup_logging()
        self.logger.info("Starting the AVAIN program")
        self.logger.info("Executed call: avain %s", " ".join(sys.argv[1:]))

        # inform user about not being root (skip for now)
        # if networks and os.getuid() != 0:
        #     print(util.MAGENTA + "Warning: not running this program as root user leads"
        #           " to a less effective assessment (e.g. with nmap)\n" + util.SANE, file=sys.stderr)

    def setup_logging(self):
        """
        Setup logging by deleting potentially old log and specifying logging format
        """
        self.logfile = os.path.abspath(os.path.join(self.output_dir, LOGFILE))
        if os.path.isfile(self.logfile):
            os.remove(
                self.logfile
            )  # delete log file if it already exists (from a previous run)
        logging.basicConfig(format=LOGGING_FORMAT,
                            filename=self.logfile,
                            level=logging.INFO)
        self.logger = logging.getLogger(__name__)

    def run(self):
        """
        Execute the main program depending on the given program parameters.
        """
        if self.update_modules:
            self.module_manager.update_modules()
            self.module_manager.reset_results()

        if self.networks or self.user_results:
            self.do_assessment()

        self.logger.info("All created files have been written to '%s'",
                         self.output_dir)  # use absolute path
        print("All created files have been written to: %s" %
              self.orig_out_dir)  # use relative path

        # change back to original directory
        os.chdir(self.original_cwd)

    def do_assessment(self):
        """
        Conduct the vulnerability assessment either in "normal" or "single network mode".
        """

        networks = self.networks
        net_dir_map = {}
        network_vuln_scores = {}

        def do_network_assessment(networks: list, out_dir: str):
            nonlocal network_vuln_scores
            self.module_manager.set_networks(networks)
            self.module_manager.set_output_dir(out_dir)
            self.module_manager.run()
            self.module_manager.create_results()
            self.module_manager.store_results()
            self.module_manager.print_results()
            net_score = self.module_manager.get_network_vuln_score()
            self.module_manager.reset_results()
            return net_score

        if self.single_network or len(networks) <= 1:
            # if there is only one assessment
            score = do_network_assessment(networks, self.output_dir)
            if self.single_network or not self.networks:
                if score is not None:
                    network_vuln_scores["assessed_network"] = score
            else:
                if score is not None:
                    network_vuln_scores[networks[0]] = score
        else:
            # if there are multiple scans, place results into separate directory
            for i, net in enumerate(networks):
                util.printit("Assessment of network '%s':" % net,
                             color=util.YELLOW)
                util.printit("===========================================",
                             color=util.YELLOW)
                net_dir_map[net] = "network_%d" % (i + 1)
                score = do_network_assessment([net],
                                              os.path.join(
                                                  self.output_dir,
                                                  net_dir_map[net]))
                network_vuln_scores[net] = score
            if net_dir_map:
                net_dir_map_out = os.path.join(self.output_dir,
                                               NET_DIR_MAP_FILE)
                with open(net_dir_map_out, "w") as file:
                    file.write(
                        json.dumps(net_dir_map, ensure_ascii=False, indent=3))

        # visualize results
        if not all(
            (not score) or score == "N/A" for score in network_vuln_scores):
            outfile = os.path.join(self.output_dir,
                                   "network_vulnerability_ratings.json")
            outfile_orig = os.path.join(self.orig_out_dir,
                                        "network_vulnerability_ratings.json")

            title = util.BRIGHT_BLUE + "Final network vulnerability scores:" + util.SANE
            visualizer.visualize_dict_results(title, network_vuln_scores,
                                              outfile)
            self.logger.info("The main output file is called '%s'", outfile)
            print("The main output file is called: %s" % outfile_orig)
Ejemplo n.º 4
0
 def register(self, obj):
     from core.module_manager import ModuleManager
     if isinstance(obj, Command):
         ModuleManager().add_command(obj, self)
         return True
     return False
Ejemplo n.º 5
0
def start():
    start_time = time.time()
    bot = Bot()
    module_manager = ModuleManager()
    logger.log(
        2, "Starting %s bot with %s %s" %
        (colored("Fusion", "magenta"), colored("Discord API", "blue"),
         colored("v" + discord.__version__, "green")))
    logger.info("Setting up django")
    django.setup()
    logger.info(
        "Loading modules from \"%s\" and \"core/modules\" directories" %
        modules_dir)
    load_modules_from_dir("core/modules", ignore={"TemplateModule"})
    load_modules_from_dir(modules_dir)

    module_manager.initialize(bot)
    logger.info("Setting up django models")
    for app in INSTALLED_APPS:
        call_command("makemigrations", app.split(".")[-1:][0])
    call_command("migrate")
    logger.info("Connecting to discord")

    @bot.event
    async def on_ready():
        logger.info("Logged into Discord as \"%s\"." % bot.user.name)
        logger.info("Running modules..")
        await module_manager.run_modules(bot)
        logger.info("Deploying threads and starting protocol processing")
        core.protocol.deploy()

        print("")
        logger.log(
            2,
            "INIT FINISHED! (took %ss)" % math.floor(time.time() - start_time))
        logger.log(2, "Loaded Modules: %s" % module_manager.modules_count)
        logger.log(2, "Loaded Commands: %s" % module_manager.commands_count)
        logger.log(
            2, "Listening %s:%s" %
            (listen_ip if listen_ip != "" else "0.0.0.0", listen_port))
        print("")

    @bot.event
    async def on_message(message: discord.Message):
        for mod in sorted(module_manager.modules.values(),
                          key=lambda x: x.on_message.priority):
            await mod.on_message(message, bot)
        if not message.content.startswith(cmd_prefix):
            return

        if message.author.bot:
            return
        args = message.content.split()
        cmd = args.pop(0)[len(cmd_prefix):].lower()

        if cmd not in module_manager.commands:
            # await bot.send_error_embed(message.channel, "Команда \"%s\" не найдена." % cmd,
            #                            "Команда не найдена")
            return

        command = module_manager.commands[cmd]
        if command.guild_lock and message.guild.id not in command.guild_lock:
            await bot.send_error_embed(
                message.channel,
                "Команда \"%s\" недоступна на данном сервере." % cmd,
                "Команда не найдена")
            return
        logger.info(
            "Выполнение команды %s от %s (%s)." %
            (repr(message.content), str(message.author), message.author.id))
        try:
            args_1, keys = parse(args)
            if not module_manager.check_permissions(message.author.guild_permissions, command.permissions) \
                    or not module_manager.check_sp_permissions(message.author.id, command.future_permissions):
                embed: discord.Embed = bot.get_error_embed(
                    "У вас недостаточно прав для выполнения данной команды",
                    "Нет прав!")
                required_perms = "\n".join(
                    perm.special_name for perm in
                    sorted(command.permissions, key=lambda x: x.value) +
                    sorted(command.future_permissions, key=lambda x: x.value))
                embed.add_field(name="Необходимые права:",
                                value=required_perms)
                await message.channel.send(embed=embed)
                await message.add_reaction(emoji_warn)
                return
            async with message.channel.typing():
                result = await command.execute(message, args_1, keys)
        except ParseError as e:
            await message.add_reaction(emoji_warn)
            await bot.send_error_embed(message.channel, str(e),
                                       "Ошибка разбора аргументов")
        except discord.Forbidden:
            await message.add_reaction(emoji_error)
            await bot.send_error_embed(message.channel, "У бота нет прав!")
        except AccessDeniedException:
            await message.add_reaction(emoji_warn)
            await bot.send_error_embed(
                message.channel,
                "У вас недостаточно прав для выполнения данной команды",
                "Нет прав!")
        except CommandException as e:
            await message.add_reaction(emoji_warn)
            await bot.send_error_embed(message.channel, str(e), e.title)
        except OpenComputersError as e:
            await bot.send_error_embed(message.channel,
                                       "```\n%s\n```" % str(e),
                                       "⚠ Криворукий уебан, у тебя ошибка! ⚠")
            await message.add_reaction(emoji_error)
        except Exception:
            await bot.send_error_embed(message.channel,
                                       "```\n%s\n```" % traceback.format_exc(),
                                       "⚠ Криворукий уебан, у тебя ошибка! ⚠")
            await message.add_reaction(emoji_error)
        else:
            if result == CommandResult.success:
                await message.add_reaction(emoji_ok)
            elif result == CommandResult.arguments_insufficient:
                embed: discord.Embed = bot.get_error_embed(
                    title="Недостаточно аргументов!")
                embed.add_field(name="%s%s %s" %
                                (cmd_prefix, command.name, command.arguments),
                                value=command.description)
                await message.channel.send(embed=embed)
                await message.add_reaction(emoji_warn)

    @bot.event
    async def on_message_delete(message: discord.Message):
        for _, mod in list(module_manager.modules.items()):
            await mod.on_message_delete(message, bot)

    @bot.event
    async def on_message_edit(before: discord.Message, after: discord.Message):
        for _, mod in list(module_manager.modules.items()):
            await mod.on_message_edit(before, after, bot)

    @bot.event
    async def on_member_remove(member: discord.Member):
        for _, mod in list(module_manager.modules.items()):
            await mod.on_member_remove(member, bot)

    @bot.event
    async def on_member_join(member: discord.Member):
        for _, mod in list(module_manager.modules.items()):
            await mod.on_member_join(member, bot)

    bot.run(discord_token)
Ejemplo n.º 6
0
    def __init__(self, networks: list, add_networks: list, omit_networks: list, update_modules: bool,
                 config_path: str, ports: list, output_dir: str, input_dir: str, user_results: dict,
                 separate_networks: bool, verbose: bool):
        """
        Create a Controller object.

        :param networks: A list of strings specifying the networks to analyze
        :param add_networks: A list of networks as strings to additionally analyze
        :param omit_networks: A list of networks as strings to omit from the analysis
        :param update_modules: Whether modules should be updated or initialized
        :param config_path: The path to a config file
        :param ports: A list of port expressions
        :param output_dir: A string specifying the output directory of the analysis
        :param input_dir: A string specifying the output directory of a previous AVAIN analysis
        :param user_results: A list of filenames whose files contain user provided results
        :param separate_networks: A boolean specifying whether all given networks are to be assessed
                                  and scored independently
        :param vebose: Specifying whether to provide verbose output or not
        """

        self.networks = networks if networks is not None else []
        self.networks += add_networks if add_networks is not None else []
        self.omit_networks = omit_networks

        # determine output directory
        if output_dir:
            self.output_dir = output_dir
        else:
            self.output_dir = "avain_output-" + util.get_current_timestamp()
        self.orig_out_dir = self.output_dir
        self.output_dir = os.path.abspath(self.output_dir)
        os.makedirs(self.output_dir, exist_ok=True)

        # copy 'modules' directory to output directory if AVAIN result directory was given as input
        if input_dir:
            if os.path.isfile(os.path.join(input_dir, NET_DIR_MAP_FILE)):
                util.printit("Error: reuse of complete output directory only supported for single network output directories", color=util.RED)
                return
            previous_modules_dir = os.path.join(input_dir, MODULE_DIR_PREFIX)
            new_modules_dir = os.path.join(self.output_dir, MODULE_DIR_PREFIX)
            if os.path.isdir(previous_modules_dir):
                if os.path.isdir(new_modules_dir):
                    shutil.rmtree(new_modules_dir)
                shutil.copytree(previous_modules_dir, new_modules_dir)

        # check for user / previous results
        self.user_results = {}
        if input_dir:
            for rtype in ResultType:
                result_file = os.path.join(RESULT_AGGR_DIRS[rtype], rtype.value.lower() + "_result.json")
                result_file = os.path.join(input_dir, result_file)
                if os.path.isfile(result_file):
                    if rtype not in self.user_results:
                        self.user_results[rtype] = []
                    self.user_results[rtype].append((result_file, os.path.abspath(result_file)))
        if user_results:
            for rtype, filenames in user_results.items():
                if rtype not in self.user_results:
                    self.user_results[rtype] = []
                for filename in filenames:
                    self.user_results[rtype].append((filename, os.path.abspath(filename)))

        # store absolute config path and config basename
        if config_path:
            config_path = os.path.abspath(config_path)
            config_base = os.path.basename(config_path)

        # change into AVAIN directory
        self.original_cwd = os.getcwd()
        core_dir = os.path.dirname(os.path.join(os.path.realpath(__file__)))
        avain_dir = os.path.abspath(os.path.join(core_dir, os.pardir))
        os.chdir(avain_dir)

        # parse default and user configs
        self.config = {}
        if os.path.isfile(DEFAULT_CONFIG_PATH):
            try:
                self.config = util.parse_config(DEFAULT_CONFIG_PATH, self.config)
            except Exception as excpt:
                print(util.MAGENTA + ("Warning: Could not parse default config file. " +
                                      "Proceeding without default config.\n") + util.SANE, file=sys.stderr)
                util.print_exception_and_continue(excpt)
        elif not config_path:
            print(util.MAGENTA + "Warning: Could not find default config.\n" + util.SANE, file=sys.stderr)

        if config_path:
            if not os.path.isfile(config_path):
                # if custom config file has no extension, check config folder for config with given name
                if not os.path.splitext(config_base)[1]:
                    pot_configs = glob.glob(os.path.join(CONFIG_DIR, config_base + "*"))
                    if pot_configs:
                        config_path = sorted(pot_configs)[0]

            # parse custom config
            try:
                self.config = util.parse_config(config_path, self.config)
            except FileNotFoundError:
                print(util.MAGENTA + ("Warning: Could not find custom config file '%s'\n" % config_path +
                                      "Proceeding without custom config\n") + util.SANE)
            except Exception as excpt:
                print(util.MAGENTA + ("Warning: Could not parse custom config file. " +
                                      "Proceeding without custom config.\n") + util.SANE, file=sys.stderr)
                util.print_exception_and_continue(excpt)

        # set remaining variables
        self.separate_networks = separate_networks
        self.verbose = verbose
        self.ports = ports
        self.update_modules = update_modules
        if (not self.update_modules) and self.config["core"]["automatic_module_updates"].lower() == "true":
            # retrieve last update timestamp based on last CPE dict download time
            last_update = util.get_creation_date("modules/resources/official-cpe-dictionary_v2.2.xml")
            passed_time = datetime.datetime.now() - last_update
            update_interval = datetime.timedelta(minutes=int(self.config["core"]["module_update_interval"]))
            if passed_time > update_interval:
                util.printit("[INFO] Module data is out-of-date and will be updated\n", color=util.MAGENTA)
                self.update_modules = True

        # setup module_manager
        self.module_manager = ModuleManager(self.networks, self.output_dir, self.omit_networks, self.ports,
                                            self.user_results, self.config, self.verbose)

        # setup logging
        self.setup_logging()
        self.logger.info("Starting the AVAIN program")
        self.logger.info("Executed call: avain %s", " ".join(sys.argv[1:]))