def check_target_is_user_by_sid(self, sid: str, domain: str) -> bool: """ 检查目标账号是否为 OU=Users """ key = sid + REDIS_KEY_SID_IS_USERS_SUFFIX record = self.redis.get_str_value(key) # 存在redis缓存记录 if record: if record == "true": return True else: return False # 不存在 则通过ldap查询,再更新redis缓存 else: ldap = LDAPSearch(domain) user_entry = ldap.search_by_sid(sid=sid, attributes=["cn"]) if user_entry: dn = user_entry.entry_dn if "OU=Users" in dn: self.redis.set_str_value( key, "true", expire=ACCOUNT_INFO_REDIS_EXPIRE_TIME) return True self.redis.set_str_value(key, "false", expire=ACCOUNT_INFO_REDIS_EXPIRE_TIME) return False
def check_target_is_admin_by_sid(self, sid: str, domain: str) -> bool: """ 检查一个账户是否拥有管理员权限 """ key = sid + REDIS_KEY_SID_IS_ADMIN_SUFFIX record = self.redis.get_str_value(key) # 存在redis缓存记录 if record: if record == "true": return True else: return False # 不存在 则通过ldap查询,再更新redis缓存 else: ldap = LDAPSearch(domain) user_entry = ldap.search_by_sid(sid=sid, attributes=["adminCount"]) if user_entry: entry_attributes = user_entry.entry_attributes_as_dict if len(entry_attributes["adminCount"] ) > 0 and entry_attributes["adminCount"][0] == 1: self.redis.set_str_value( key, "true", expire=ACCOUNT_INFO_REDIS_EXPIRE_TIME) return True self.redis.set_str_value(key, "false", expire=ACCOUNT_INFO_REDIS_EXPIRE_TIME) return False
def user_is_sensitive_by_sid(self, sid: str, domain: str) -> bool: """ 检查某个用户是否为敏感用户 1. adminCount 1 2. 属于敏感组 3. 蜜罐账户 4. 自定义敏感用户 LDAP查询是性能瓶颈,需要使用redis进行缓存加速 """ # 蜜罐账户 for user in main_config.honeypot_account: if user["sid"] == sid: return True # 自定义敏感用户 for user in main_config.sensitive_users: if user["sid"] == sid: return True # 先查缓存 _cache_is_sensitive = self.get_target_sensitive_cache(sid) if _cache_is_sensitive is not None: return _cache_is_sensitive == "true" # LDAP 查询较慢,性能瓶颈 ldap = LDAPSearch(domain) user_entry = ldap.search_by_sid(sid, attributes=["adminCount", "memberOf"]) if not user_entry: self.set_target_sensitive_cache(sid, "false") return False # adminCount if len(user_entry.entry_attributes_as_dict["adminCount"]) > 0 and \ user_entry.entry_attributes_as_dict["adminCount"][0] == 1: self.set_target_sensitive_cache(sid, "true") return True # 敏感组 groups = user_entry.entry_attributes_as_dict["memberOf"] sensitive_groups = list( map(lambda x: x["name"], main_config.sensitive_groups)) for g in groups: g_name = get_cn_from_dn(g) if g_name in sensitive_groups: self.set_target_sensitive_cache(sid, "true") return True self.set_target_sensitive_cache(sid, "false") return False
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 = ldap.search_by_sid(target_sid, attributes=["cn"]) if not target: return target_user_name = str(target["cn"]) return self._generate_alert_doc(target_user_name=target_user_name)
def get_user_info_by_sid(self, sid: str, domain: str) -> User: key = sid + REDIS_KEY_SID_USERNAME_SUFFIX # 先查redis user_name = self.redis.get_str_value(key) if not user_name: ldap = LDAPSearch(domain) user_entry = ldap.search_by_sid(sid, attributes=["sAMAccountName"]) if not user_entry: return None user_name = user_entry.entry_attributes_as_dict["sAMAccountName"][ 0] self.redis.set_str_value(key, user_name, expire=ACCOUNT_INFO_REDIS_EXPIRE_TIME) user = User({ "user_name": user_name, "user_sid": sid, "logon_id": "", "domain_name": domain }) return user
def user_is_sensitive_by_sid(self, sid: str, domain: str) -> bool: """ 检查某个用户是否为敏感用户 1. adminCount 1 2. 属于敏感组 3. 蜜罐账户 4. 自定义敏感用户 """ # 蜜罐账户 for user in main_config.honeypot_account: if user["sid"] == sid: return True # 自定义敏感用户 for user in main_config.sensitive_users: if user["sid"] == sid: return True ldap = LDAPSearch(domain) user_entry = ldap.search_by_sid(sid, attributes=["adminCount", "memberOf"]) if not user_entry: return False # adminCount if len(user_entry.entry_attributes_as_dict["adminCount"]) > 0 and \ user_entry.entry_attributes_as_dict["adminCount"][0] == 1: return True # 敏感组 groups = user_entry.entry_attributes_as_dict["memberOf"] sensitive_groups = list( map(lambda x: x["name"], main_config.sensitive_groups)) for g in groups: g_name = get_cn_from_dn(g) if g_name in sensitive_groups: return True return False
def _get_group_name(self, sid, domain): ldap = LDAPSearch(domain) entry = ldap.search_by_sid(sid, attributes=["cn"]) if entry: return str(entry["cn"])