Example #1
0
 def __init__(self):
     super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
     self.parser = SDDLParser()
     self.account_info = AccountInfo()
     self.watch_object_class = [
         "container", "domainDNS", "groupPolicyContainer"
     ]
class ModifySensitiveGroup(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.account_info = AccountInfo()

    def run(self, log: Log):
        self.init(log=log)

        group_name = log.target_info.user_name

        sensitive_groups = list(
            map(lambda x: x["name"], main_config.sensitive_groups))
        if group_name in sensitive_groups:
            # 添加到了敏感组,更新redis缓存
            self.account_info.set_target_sensitive_cache(
                self.log.event_data["MemberSid"], "true")
            return self._generate_alert_doc()

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_ip": source_ip,
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "group_name": self.log.target_info.user_name,
            "group_sid": self.log.event_data["TargetSid"],
            "target_user_name":
            get_cn_from_dn(self.log.event_data["MemberName"]),
            "target_user_dn": self.log.event_data["MemberName"],
            "target_user_sid": self.log.event_data["MemberSid"],
            "target_domain": self.log.target_info.domain_name,
            "source_user_name": self.log.subject_info.user_name,
            "source_user_sid": self.log.subject_info.user_sid,
            "source_logon_id": self.log.subject_info.logon_id,
            "source_domain": self.log.subject_info.domain_name,
            "privilege_list": self.log.event_data["PrivilegeList"]
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return HIGH_LEVEL
Example #3
0
class SPNChange(object):
    def __init__(self):
        self.account_info = AccountInfo()
        self.account_activity = AccountActivity(activity_type=SPN_CHANGE)

    def run(self, log: Log):
        if log.event_data["AttributeLDAPDisplayName"] != "servicePrincipalName":
            return

        if log.event_data["ObjectClass"] != "user":
            return

        if log.event_data["OperatorType"] not in ["%%14675", "%%14674"]:
            return

        domain = log.event_data["DSName"]
        target_user_name = get_cn_from_dn(log.object_info.dn)
        target_user_info = self.account_info.get_user_info_by_name(
            target_user_name, domain)

        form_data = {
            "operator": {
                "user_name": log.subject_info.user_name,
                "sid": log.subject_info.user_sid,
                "logon_id": log.subject_info.logon_id
            },
            "operator_type":
            "add" if log.event_data["OperatorType"] == "%%14674" else "remove",
            "value":
            log.event_data["AttributeValue"],
            "object_dn":
            log.object_info.dn
        }

        self.account_activity.save_activity(domain=log.event_data["DSName"],
                                            user_name=target_user_name,
                                            sid=target_user_info.user_sid,
                                            dc_name=log.dc_host_name,
                                            timestamp=log.utc_log_time,
                                            data=form_data)
Example #4
0
 def __init__(self):
     super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
     self.account_info = AccountInfo()
     self.account_history = AccountHistory()
Example #5
0
class SearchUserDetail(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.account_info = AccountInfo()
        self.account_history = AccountHistory()

    def run(self, log: Log):
        self.init(log=log)

        if log.object_info.type != "SAM_USER":
            return

        user_name = log.subject_info.user_name
        if user_name.endswith("$"):
            return

        target_sid = log.object_info.name
        if not target_sid.startswith("S-1-5-21-"):
            return

        # 查询自身也忽略
        if log.subject_info.user_sid == target_sid:
            return

        # 判断域名是否存在于需要检查的域名中
        domain_name = log.subject_info.domain_name
        if filter_domain(domain_name):
            return

        # 如果账号是管理员 直接忽略
        if self.account_info.check_target_is_admin_by_sid(
                sid=log.subject_info.user_sid, domain=domain_name):
            return

        # 判断操作者是否为Users
        if not self.account_info.check_target_is_user_by_name(
                user_name, domain_name):
            return

        # 判断是否为敏感用户
        if not self.account_info.user_is_sensitive_by_sid(sid=target_sid,
                                                          domain=domain_name):
            return

        ldap = LDAPSearch(domain_name)
        target_user_name = str(
            ldap.search_by_sid(target_sid, attributes=["cn"])["cn"])

        return self._generate_alert_doc(target_user_name=target_user_name)

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "source_ip": source_ip,
            "source_user_name": self.log.subject_info.user_name,
            "source_user_sid": self.log.subject_info.user_sid,
            "source_logon_id": self.log.subject_info.logon_id,
            "source_domain": self.log.subject_info.domain_name,
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return LOW_LEVEL
Example #6
0
class NTLMRelay(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.account_history = AccountHistory()
        self.account_info = AccountInfo()
        self.es = ElasticHelper()

    def run(self, log: Log):
        self.init(log=log)

        # 处于数据统计时间内,不检测
        if datetime_now_obj() < main_config.learning_end_time:
            return

        if not log.source_info.ip_address:
            return

        if log.event_data["AuthenticationPackageName"] != "NTLM":
            return

        work_station = log.source_info.work_station_name
        netbios_name = get_netbios_domain(log.target_info.domain_name)
        if filter_domain(netbios_name):
            return

        # 为较小误报 目前只考虑来源主机为敏感主机的行为
        if not self.account_info.computer_is_sensitive_by_name(
                work_station, domain=netbios_name):
            return

        ip_address = log.source_info.ip_address
        if ip_filter(ip_address):
            return

        # 根据主机名去查最近的认证IP
        last_ip = self.account_history.search_last_ip_by_workstation(
            work_station)
        if not last_ip or last_ip == "unknown" or last_ip == ip_address:
            return

        # 二次确认,如果上次认证IP与当前IP不相同,则对主机名进行解析,判断IP是否相等
        resolver_ips = self._get_host_ip(log)
        if ip_address in resolver_ips:
            return

        if "V1" in log.event_data["LmPackageName"]:
            version = "v1"
        else:
            version = "v2"

        relay_workstation = self.account_history.get_last_workstation_by_ip(
            ip_address)

        return self._generate_alert_doc(relay_workstation=relay_workstation,
                                        ntlm_version=version,
                                        resolver_ips=resolver_ips,
                                        last_ip=last_ip)

    def _generate_alert_doc(self, **kwargs) -> dict:
        form_data = {
            "source_ip": self.log.source_info.ip_address,
            "relay_ip": self.log.source_info.ip_address,
            "source_workstation": self.log.source_info.work_station_name,
            "target_user_name": self.log.target_info.user_name,
            "target_domain": self.log.target_info.domain_name,
            "target_user_sid": self.log.target_info.user_sid,
            "target_logon_id": self.log.target_info.logon_id,
            "dc_hostname": self.log.dc_host_name,
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.source_info.ip_address),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return HIGH_LEVEL

    def _get_host_ip(self, log: Log) -> list:
        """
            对主机名进行DNS解析
        """
        dns_name = "{workstation}.{domain}".format(
            workstation=log.source_info.work_station_name,
            domain=self.get_FQDN_domain(log.target_info.domain_name))
        try:
            ip_list = get_ip_from_domain(dns_name)
        except Exception as e:
            return []
        return ip_list

    def get_FQDN_domain(self, domain) -> str:
        for each in main_config.domain_list:
            if each.startswith(domain.lower()):
                return each
        return ""
Example #7
0
 def __init__(self):
     super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
     self.es = ElasticHelper()
     self.account_info = AccountInfo()
Example #8
0
class PsLoggedOn(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.es = ElasticHelper()
        self.account_info = AccountInfo()

    def run(self, log: Log):
        self.init(log=log)

        if log.event_data["ShareName"] != r"\\*\IPC$":
            return

        if log.event_data["RelativeTargetName"] != "srvsvc":
            return

        # 忽略域管理员的访问
        if self.account_info.check_target_is_admin_by_sid(
                sid=log.subject_info.user_sid,
                domain=log.subject_info.domain_name):
            return

        # 存在向前查找,则延迟确认
        if "_delay_info" not in log.record:
            self.delay_confirm_log()
            return
        self.es.wait_log_in_database(log.dc_computer_name, log.record_number)

        is_match = self._search_forward(log)
        if is_match:
            return self._generate_alert_doc()

    def _generate_alert_doc(self, **kwargs) -> dict:
        form_data = {
            "source_workstation":
            self._get_workstation_by_source_ip(
                self.log.source_info.ip_address),
            "source_ip":
            self.log.source_info.ip_address,
            "source_user_name":
            self.log.subject_info.user_name,
            "source_user_sid":
            self.log.subject_info.user_sid,
            "source_logon_id":
            self.log.subject_info.logon_id,
            "source_domain":
            self.log.subject_info.domain_name,
            "dc_hostname":
            self.log.dc_host_name,
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.source_info.ip_address),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return LOW_LEVEL

    def _search_forward(self, log: Log):
        """

        """
        base = {
            "index": ElasticConfig.event_log_index,
            "doc_type": ElasticConfig.event_log_doc_type
        }
        # 开始向前查找
        msearch_body = [base]
        body_winreg = self._get_query(log, "winreg")
        msearch_body.append(body_winreg)

        msearch_body.append(base)
        body_lsarpc = self._get_query(log, "lsarpc")
        msearch_body.append(body_lsarpc)

        results = self.es.multi_search(
            body=msearch_body,
            index=ElasticConfig.event_log_index,
            doc_type=ElasticConfig.event_log_doc_type)
        for each in results["responses"]:
            if each.get("error"):
                logger.error("PsLoggedOn module - multi search error: " +
                             each.get("error").get("reason"))
                raise MsearchException()
            elif each["hits"]["total"] == 0:
                return False
        return True

    def _get_query(self, log: Log, relative_target_name):
        computer_term = get_term_statement("computer_name",
                                           log.dc_computer_name),
        ago_time = move_n_min(utc_to_datetime(log.utc_log_time), 1)
        time_str = datetime_to_utc(ago_time)
        logon_id_term = get_term_statement("event_data.SubjectLogonId.keyword",
                                           log.subject_info.logon_id)
        share_name_term = get_term_statement("event_data.ShareName.keyword",
                                             r"\\*\IPC$")
        time_term = get_time_range("gt", time_str),
        relative_term = get_term_statement(
            "event_data.RelativeTargetName.keyword", relative_target_name)
        query = {
            "query":
            get_must_statement(logon_id_term, share_name_term, relative_term,
                               time_term, computer_term),
            "_source":
            False,
            "size":
            1
        }
        return query
Example #9
0
 def __init__(self):
     self.account_info = AccountInfo()
     self.account_activity = AccountActivity(activity_type=SPN_CHANGE)
Example #10
0
class EnumerateGroup(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.account_info = AccountInfo()

    def run(self, log: Log):
        self.init(log=log)

        if log.object_info.server != "Security Account Manager":
            return

        if log.object_info.type != "SAM_GROUP":
            return

        sid = log.object_info.name
        if not sid.startswith("S-1-5-21-"):
            return

        user_name = log.subject_info.user_name
        if user_name.endswith("$"):
            return

        # 如果账号是管理员 直接忽略
        if self.account_info.check_target_is_admin_by_sid(
                sid=sid, domain=log.subject_info.domain_name):
            return

        # 判断账号是否为 Users,如果不是,直接退出
        if not self.account_info.check_target_is_user_by_name(
                user_name, log.subject_info.domain_name):
            return

        group_name = self._get_group_name(sid, log.subject_info.domain_name)
        if not group_name:
            return

        sensitive_groups = list(
            map(lambda x: x["name"], main_config.sensitive_groups))
        if group_name in sensitive_groups:
            return self._generate_alert_doc(group_name=group_name)

    def _get_group_name(self, sid, domain):
        ldap = LDAPSearch(domain)
        entry = ldap.search_by_sid(sid, attributes=["cn"])
        if entry:
            return str(entry["cn"])

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "source_ip": source_ip,
            "source_user_name": self.log.subject_info.user_name,
            "source_user_sid": self.log.subject_info.user_sid,
            "source_logon_id": self.log.subject_info.logon_id,
            "source_domain": self.log.subject_info.domain_name,
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return LOW_LEVEL
Example #11
0
 def __init__(self):
     super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
     self.delegation = Delegation()
     self.account_info = AccountInfo()
Example #12
0
class ResBasedConsDelegation(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.delegation = Delegation()
        self.account_info = AccountInfo()

    def run(self, log: Log):
        self.init(log=log)

        if log.event_data[
                "AttributeLDAPDisplayName"] != "msDS-AllowedToActOnBehalfOfOtherIdentity":
            return

        # 只检测敏感计算机
        account = get_cn_from_dn(log.object_info.dn)
        domain = get_domain_from_dn(log.object_info.dn)
        if not self.account_info.computer_is_sensitive_by_name(
                account, domain=get_netbios_domain(domain)):
            return

        ldap = LDAPSearch(domain=domain)
        entry = ldap.search_by_cn(
            cn=account,
            attributes=["sid", "msDS-AllowedToActOnBehalfOfOtherIdentity"])
        if entry is None:
            return
        entry_sid = str(entry["sid"])

        sd = SR_SECURITY_DESCRIPTOR(entry.entry_attributes_as_dict[
            "msDS-AllowedToActOnBehalfOfOtherIdentity"][0])
        # 拥有特殊DACL权限的SID列表
        ace_list = []
        for ace in sd["Dacl"].aces:
            ace_list.append({
                "type_name": ace["TypeName"],
                "sid": ace['Ace']['Sid'].formatCanonical()
            })
        sid_list = list(map(lambda ace: ace["sid"], ace_list))
        sid_list = sorted(list(set(sid_list)))

        target_account_info = User({
            "user_name": account,
            "user_sid": entry_sid
        })

        # 查询历史委派记录
        record = self.delegation.find_res_constrained_delegation_by_name(
            name=account)
        # 不存在记录 则新建 并直接告警
        if not record:
            self.delegation.new_delegation_record(
                user=target_account_info,
                delegation_type=RES_BASED_CONSTRAINED_DELEGATION,
                allowed_to=sid_list)
            return self._generate_alert_doc(
                target_computer=account,
                target_user_name=target_account_info.user_name,
                target_user_sid=target_account_info.user_sid,
                add_allowed_sid=sid_list,
                old_allowed_sid=[])

        # 存在记录且不变,退出
        if sid_list == record["allowed_to"]:
            return

        # 存在记录 对比历史的sid 无新增 更新记录 退出
        new_sids = self._get_new_sid(new_list=sid_list,
                                     old_list=record["allowed_to"])
        if len(new_sids) == 0:
            self.delegation.update_delegation(
                sid=entry_sid,
                delegation_type=RES_BASED_CONSTRAINED_DELEGATION,
                allowed_to=sid_list)
            return

        # 存在记录 有新增 更新记录 告警
        if len(new_sids) > 0:
            self.delegation.update_delegation(
                sid=entry_sid,
                delegation_type=RES_BASED_CONSTRAINED_DELEGATION,
                allowed_to=sid_list)
            return self._generate_alert_doc(
                target_computer=account,
                target_user_name=target_account_info.user_name,
                target_user_sid=target_account_info.user_sid,
                add_allowed_sid=new_sids,
                old_allowed_sid=record["allowed_to"])

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "source_ip": source_ip,
            "source_user_name": self.log.subject_info.user_name,
            "source_domain": self.log.subject_info.domain_name,
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return HIGH_LEVEL

    def _get_new_sid(self, new_list, old_list) -> list:
        result = []
        for each in new_list:
            if each not in old_list:
                result.append(each)
        return result
Example #13
0
class UnknownAdminLogin(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.account_info = AccountInfo()

    def run(self, log: Log):
        self.init(log=log)

        sid = log.subject_info.user_sid
        user_name = log.subject_info.user_name
        domain_name = log.subject_info.domain_name

        if domain_name.lower() == "window manager":
            return

        if not self._is_in_domain_list(domain_name):
            return

        if len(log.subject_info.user_sid.split("-")) == 4:
            return

        # 排除域控计算机账户的本地特权登录
        if user_name.endswith("$"):
            domain = get_netbios_domain(domain_name)
            if user_name[:-1] in main_config.dc_name_list[domain]:
                return

        if self.account_info.check_target_is_admin_by_sid(sid=sid,
                                                          domain=domain_name):
            return

        return self._generate_alert_doc()

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "source_ip": source_ip,
            "target_user_name": self.log.subject_info.user_name,
            "target_user_sid": self.log.subject_info.user_sid,
            "target_logon_id": self.log.subject_info.logon_id,
            "target_domain": self.log.subject_info.domain_name,
            "dc_hostname": self.log.dc_host_name,
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return HIGH_LEVEL

    def _is_in_domain_list(self, domain: str) -> bool:
        """
            域名是否在已知需要监控的域列表中
        """
        domain = get_netbios_domain(domain)
        for each in main_config.domain_list:
            if domain == get_netbios_domain(each):
                return True
        return False
Example #14
0
class ACLModify(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.parser = SDDLParser()
        self.account_info = AccountInfo()
        self.watch_object_class = [
            "container", "domainDNS", "groupPolicyContainer"
        ]

    def run(self, log: Log):
        self.init(log=log)

        if log.object_info.class_ not in self.watch_object_class:
            return

        if log.event_data["AttributeLDAPDisplayName"] != "nTSecurityDescriptor":
            return

        abnormal_ace_list = []
        abnormal_users = []
        content = self.parser.parse(log.event_data["AttributeValue"])
        domain = log.subject_info.domain_name
        for ace in content["dacl_ace_list"]:
            trustee = ace["trustee"]
            # 判断是否为SID,因为这种ACL几乎都是默认的用户,很少特殊指定用户
            if trustee.startswith("S-1-5-21-"):
                # 首先检查 该SID是否为某个用户?(Users),如果不是,则忽略掉
                if not self.account_info.check_target_is_user_by_sid(
                        trustee, domain):
                    continue
                # 获取用户信息
                user = self.account_info.get_user_info_by_sid(sid=trustee,
                                                              domain=domain)
                # 目标是否为管理员权限
                if self.account_info.check_target_is_admin_by_sid(
                        trustee, domain):
                    continue
                if user.user_name in abnormal_users:
                    continue

                abnormal_users.append(user.user_name)
                abnormal_ace = self._get_abnormal_ace(ace, domain)
                abnormal_ace_list.append(abnormal_ace)

        if len(abnormal_ace_list) > 0:
            return self._generate_alert_doc(
                object_class=log.object_info.class_,
                abnormal_ace_list=abnormal_ace_list,
                parsed_sddl=content,
                abnormal_users=abnormal_users)

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "source_ip": source_ip,
            "source_user_name": self.log.subject_info.user_name,
            "source_user_sid": self.log.subject_info.user_sid,
            "source_logon_id": self.log.subject_info.logon_id,
            "source_domain": self.log.subject_info.domain_name,
            "object_info": self.log.object_info.get_doc(),
            **kwargs
        }

        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return HIGH_LEVEL

    def _get_abnormal_ace(self, ace, domain) -> dict:
        trustee = ace["trustee"]
        ace["user_name"] = "unknown"
        user_info = self.account_info.get_user_info_by_sid(sid=trustee,
                                                           domain=domain)
        if user_info:
            ace["user_name"] = user_info.user_name
        return copy.deepcopy(ace)
Example #15
0
class GPODelegation(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)
        self.account_info = AccountInfo()

    def run(self, log: Log):
        self.init(log=log)

        if not log.object_info.dn:
            return
        assert isinstance(log.object_info.dn, str)
        # 组策略对象
        if not log.object_info.dn.lower().startswith("cn=policies,cn=system,"):
            return
        if not log.object_info.class_ == "container":
            return

        value = log.event_data["AttributeValue"]
        parser = SDDLParser()

        value_obj = parser.parse(value)

        domain = log.subject_info.domain_name

        abnormal_ace_list = []
        abnormal_users = []
        for each in value_obj["dacl_ace_list"]:
            trustee = each.get("trustee")
            if trustee.startswith("S-1-5-21-"):
                # 首先检查 该SID是否为某个用户?(Users),如果不是,则忽略掉
                if not self.account_info.check_target_is_user_by_sid(
                        trustee, domain):
                    continue
                user = self.account_info.get_user_info_by_sid(trustee, domain)
                abnormal_users.append(user.user_name)
                abnormal_ace_list.append(each)

        if len(abnormal_ace_list) > 0:
            return self._generate_alert_doc(
                abnormal_ace_list=abnormal_ace_list,
                parsed_sddl=value_obj,
                abnormal_users=abnormal_users)

    def _generate_alert_doc(self, **kwargs) -> dict:
        source_ip = self._get_source_ip_by_logon_id(
            self.log.subject_info.logon_id,
            self.log.subject_info.full_user_name)
        form_data = {
            "source_ip": source_ip,
            "source_workstation":
            self._get_workstation_by_source_ip(source_ip),
            "source_user_name": self.log.subject_info.user_name,
            "source_user_sid": self.log.subject_info.user_sid,
            "source_logon_id": self.log.subject_info.logon_id,
            "source_domain": self.log.subject_info.domain_name,
            "object_info": self.log.object_info.get_doc(),
            **kwargs
        }
        doc = self._get_base_doc(level=self._get_level(),
                                 unique_id=self._get_unique_id(
                                     self.code,
                                     self.log.subject_info.user_name),
                                 form_data=form_data)
        return doc

    def _get_level(self) -> str:
        return HIGH_LEVEL