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)
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