Example #1
0
def load_settings():
    """
        加载Mongo中保存的配置信息到redis中
    """
    mongo = MongoHelper(MongoConfig.uri, MongoConfig.db,
                        MongoConfig.settings_collection)
    redis = RedisHelper()
    fetcher = mongo.find_all({})
    for each in fetcher:
        key = each["name"] + "_setting"
        # 再录入
        if isinstance(each["value"], list):
            if len(each["value"]) == 0:
                continue
            elif isinstance(each["value"][0], dict):
                redis.set_str_value(key, simplejson.dumps(each["value"]))
                continue
            redis.set_list(key, *each["value"])
        elif isinstance(each["value"], dict):
            redis.set_str_value(key, simplejson.dumps(each["value"]))
        else:
            redis.set_str_value(key, each["value"])
Example #2
0
class MatchRules(object):
    def __init__(self, rules_table):
        self.mongo = MongoHelper(MongoConfig.uri, MongoConfig.db, rules_table)

    def match(self, event_doc: dict):
        alert_code = event_doc["alert_code"]
        form_data = event_doc["form_data"]
        all_rules = self.mongo.find_all({"alert_code": alert_code})
        for rule in all_rules:
            try:
                rule = Rule(rule)
                # 成功匹配规则 则返回被匹配上的规则ID
                if self._match_one(rule, form_data):
                    return rule.id
            except Exception as e:
                return False
        return False

    def _match_one(self, rule: Rule, form_data):
        """
            一条忽略内容的多条规则,全部匹配才算成功
        """
        for rule_content in rule.rules:
            if rule_content.field_type == "string":
                if not self._match_string(form_data[rule_content.field_name],
                                          rule_content.value,
                                          rule_content.match_type):
                    return False
            elif rule_content.field_type == "ip":
                if not self._match_ip(form_data[rule_content.field_name],
                                      rule_content.value):
                    return False
            elif rule_content.field_type == "list":
                if not self._match_list(form_data[rule_content.field_name],
                                        rule_content.value,
                                        rule_content.match_type):
                    return False
        return True

    def _match_string(self, alert_value, rule_value, match_type) -> bool:
        """
            匹配字符串,将告警字段内容和忽略规则的内容匹配
        """
        if match_type == "term":
            if isinstance(alert_value, list):
                for each in alert_value:
                    if each == rule_value:
                        return True
                return False
            else:
                return alert_value == rule_value
        # 正则匹配
        elif match_type == "regex":
            if isinstance(alert_value, list):
                for each in alert_value:
                    if re.match(rule_value, each):
                        return True
                return False
            else:
                return True if re.match(rule_value, alert_value) else False

    def _match_ip(self, alert_ip, rule_ip) -> bool:
        """
            匹配IP类型
        """
        # CIDR
        if "/" in rule_ip:
            return IP(alert_ip) in IP(rule_ip)
        else:
            return alert_ip == rule_ip

    def _match_list(self, alert_value, rule_value, match_type):
        """
            匹配列表
        """
        for each in rule_value:
            if match_type == "ip" and self._match_ip(alert_value, each):
                return True
            elif match_type == "string" or match_type == "regex":
                if self._match_string(alert_value, each, match_type):
                    return True
        return False
Example #3
0
class Engine(object):
    def __init__(self):
        self.event_log_modules_map = None
        # self.traffic_kerberos_modules_map = None
        self.mongo = MongoHelper(MongoConfig.uri, MongoConfig.db,
                                 MongoConfig.delay_run_collection)
        self.alert = Alert()

    def load(self):
        # 加载事件日志检测模块
        logger.info("loading detect modules based on event_log")
        self.event_log_modules_map = self._load_module("event_log", "EVENT_ID")

        # 加载kerberos流量检测模块
        # logger.info("loading detect modules based on traffic_kerberos")
        # self.traffic_kerberos_modules_map = self._load_module("traffic_kerberos", "MSG_TYPE")

    def start(self):
        """
            引擎启动主入口
        """
        self.load()

        # 启动消费者
        c = Consumer()
        # 注册回调
        logger.info("start MQ consumer and register callback func.")
        logger.info("status: main process running")
        c.run(self.do_analyze)

    def delay_run(self):
        """
            延迟检测

            ** 请单进程运行! **
        """
        self.load()
        logger.info("status: delay process running")
        while True:
            time.sleep(5)
            data_list = self._get_delay_data()
            for data in data_list:
                alert_code = data["_delay_info"]["alert_code"]
                # if data["type"] == "krb5":
                #     krb = Kerberos(data)
                #     self._run_analyze(data=krb, data_type=krb.msg_type, modules_map=self.traffic_kerberos_modules_map,
                #                       alert_code=alert_code)
                if data["type"] == "wineventlog":
                    log = Log(data)
                    self._run_analyze(data=log,
                                      data_type=log.event_id,
                                      modules_map=self.event_log_modules_map,
                                      alert_code=alert_code)
            # 删除完成检测数据
            self._clear_confirmed_data(data_list)

    def do_analyze(self, data: dict):
        # 解析krb5流量
        # if data["type"] == "krb5":
        #     krb = Kerberos(data)
        #     if krb.msg_type not in self.traffic_kerberos_modules_map:
        #         return
        #     self._run_analyze(data=krb, data_type=krb.msg_type, modules_map=self.traffic_kerberos_modules_map)
        # 解析事件日志
        if data["type"] == "wineventlog":
            if data["event_id"] == 4662:
                return
            if "event_data" not in data and data["event_id"] != 1100:
                return
            log = Log(data)
            if log.event_id not in self.event_log_modules_map:
                return
            self._run_analyze(data=log,
                              data_type=log.event_id,
                              modules_map=self.event_log_modules_map)

    def _run_analyze(self,
                     data,
                     data_type,
                     modules_map: dict,
                     alert_code=None):
        """
            运行检测模块
        :param data: 数据字典
        :param data_type: log.event_id 的值或者 krb.msg_type
        :param modules_map: 加载了检测模块的字典
        :param alert_code:  可选,具体检测的告警代码,指定了之后只运行该模块
        :return:
        """
        module_list = modules_map[data_type]
        for module in module_list:
            code = module["code"]
            if alert_code and alert_code != code:
                continue
            m_object = module["object"]
            # 运行检测模块的语句
            alert_doc = m_object.run(data)
            if alert_doc:
                # 存在问题,告警
                self.alert.generate(alert_doc)

    def _load_module(self, name: str, data_type: str) -> dict:
        modules_map = {}

        def _register_module(d_type, m):
            if d_type not in modules_map:
                modules_map[d_type] = [m]
            else:
                modules_map[d_type].append(m)

        file_list = get_walk_files(project_dir + "/modules/detect/" + name)

        for f in file_list:
            f = f.replace(project_dir, ".")
            module_path, f = format_module_path(f)
            module = __import__(module_path, fromlist=[f])
            logger.info("loaded module: " + module_path)
            data_types = getattr(module, data_type)
            assert isinstance(data_types, list)
            for d_type in data_types:
                _register_module(
                    d_type, {
                        "code":
                        getattr(module, "ALERT_CODE") if hasattr(
                            module, "ALERT_CODE") else None,
                        "object":
                        getattr(module, f)()
                    })
        return modules_map

    def _get_delay_data(self):
        query = {"_delay_info.time": {"$lte": datetime_now_obj()}}
        return [each for each in self.mongo.find_all(query)]

    def _clear_confirmed_data(self, data_list):
        id_list = []
        for data in data_list:
            id_list.append(data["_id"])
        query = {"_id": {"$in": id_list}}
        self.mongo.delete_many(query)
Example #4
0
class Activity(object):
    def __init__(self):
        self.activity_mongo = MongoHelper(MongoConfig.uri, MongoConfig.db, MongoConfig.activities_collection)
        self.invasion = Invasion()

    def new(self, activity_doc: dict) -> ObjectId:
        """
            新增威胁活动记录

            同时尝试生成入侵事件记录
        """
        invasion_id = self._generate_invasion(activity_doc)
        if invasion_id:
            activity_doc["invasion_id"] = invasion_id
        return self.activity_mongo.insert_one(activity_doc).inserted_id

    def add_alert(self, activity_doc, alert_doc: dict):
        doc = {
            "$set": {}
        }
        if alert_doc["end_time"] > activity_doc["end_time"]:
            doc["$set"]["end_time"] = alert_doc["end_time"]
        if alert_doc["level"] > activity_doc["level"]:
            doc["$set"]["level"] = alert_doc["level"]
        if len(doc["$set"].keys()) == 0:
            return
        self.activity_mongo.update_one({
            "_id": activity_doc["_id"]
        }, doc)

    def update(self, _id, doc):
        self.activity_mongo.update_one({
            "_id": _id
        }, doc=doc)

    def find_record(self, uid, start_time):
        """
            根据 unique_id 查找一段时间内相同的威胁活动
        """
        return self.activity_mongo.find_one({
            "unique_id": uid,
            "end_time": {"$gte": start_time + timedelta(hours=-main_config.merge_activity_time)}
        })

    def _generate_invasion(self, activity_doc) -> ObjectId:
        """
            生成入侵事件
            多个相同来源IP 不同类型的威胁活动 可以生成一个入侵事件

            返回入侵事件的ID
        """
        # 首先查找是否已存在对应的入侵事件,尝试合并
        invasion_id = self._merge_activity(activity_doc)
        if invasion_id:
            assert isinstance(invasion_id, ObjectId)
            return invasion_id

        # 没有已存在的入侵事件,则按照条件,查找是否已存在不同类型的威胁活动
        query = {
            "form_data.source_ip": activity_doc["form_data"]["source_ip"],
            "end_time": {"$gte": activity_doc["start_time"] + timedelta(hours=-main_config.merge_invasion_time)},
            "alert_code": {"$ne": activity_doc["alert_code"]}
        }

        # 如果存在不同类型的威胁活动
        another_activities = list(self.activity_mongo.find_all(query).sort("start_time", ASCENDING))
        if len(another_activities) > 0:
            invasion_id = self.invasion.new(*another_activities, activity_doc)
            # 创建完入侵事件之后,将之前查询的到威胁活动全部加上对应的ID
            for activity in another_activities:
                self.update(activity["_id"], {
                    "$set": {
                        "invasion_id": invasion_id
                    }
                })

    def _merge_activity(self, activity_doc):
        source_ip = activity_doc["form_data"]["source_ip"]
        invasion = self.invasion.find_record(source_ip, activity_doc["start_time"])
        if not invasion:
            return False

        # 向存在的入侵事件添加当前威胁活动
        self.invasion.add_activity(invasion["_id"], activity_doc)
        return invasion["_id"]