def init_sensitive_groups(domain): logger.info("init sensitive groups.") domain = get_netbios_domain(domain) ldap_search = LDAPSearch(domain) redis = RedisHelper() mongo = MongoHelper(uri=MongoConfig.uri, db=MongoConfig.db, collection=MongoConfig.settings_collection) sensitive_groups = [] for item in default_sensitive_groups(domain): if len(item["sid"]) > 0: sensitive_groups.append(item) else: entry = ldap_search.search_by_name(item["name"], attributes=["objectSid"]) if not entry or len( entry.entry_attributes_as_dict["objectSid"]) == 0: continue sid = entry.entry_attributes_as_dict["objectSid"][0] item["sid"] = sid sensitive_groups.append(item) logger.info(",".join(list(map(lambda x: x["name"], sensitive_groups)))) sensitive_entry = mongo.find_one({"name": "sensitive_entry"})["value"] sensitive_entry["group"] = sensitive_groups mongo.update_one({"name": "sensitive_entry"}, {"$set": { "value": sensitive_entry }}, upsert=True) redis.set_str_value("sensitive_entry" + REDIS_KEY_SUFFIX, simplejson.dumps(sensitive_entry))
class TicketRecords(object): def __init__(self): self.es = ElasticHelper() self.redis = RedisHelper() def save_ticket(self, ticket_doc): # 首先将当前票据保存到redis缓存中 self.redis.set_str_value(ticket_doc["ticket_data"]["ticket_hash"], ticket_doc["ticket_type"], expire=60 * 60 * main_config.TGT_maximum_lifetime) index = ElasticConfig.krb5_ticket_write_index_prefix + datetime_to_log_date( datetime_now_obj()) # 保存到ES中 self.es.delay_index(body=ticket_doc, index=index, doc_type=ElasticConfig.krb5_ticket_doc_type) def exist_ticket_by_hash(self, ticket_hash: str, ticket_type: str) -> bool: result = self.redis.get_str_value(ticket_hash) if result and result == ticket_type: return True else: return False def set_username_by_tgt_hash(self, ticket_hash: str, username: str): key = ticket_hash + REDIS_TICKET_HASH_USERNAME_SUFFIX return self.redis.set_str_value(key, username, expire=60 * 60 * 24 * 7) def get_username_by_tgt_hash(self, ticket_hash: str) -> str: key = ticket_hash + REDIS_TICKET_HASH_USERNAME_SUFFIX username = self.redis.get_str_value(key) if not username: return "unknown" else: return username def terms_by_custom(self, query: dict, aggs_field: str, aggs_size) -> list: body = { "query": query, "size": 0, "aggs": get_aggs_statement("abc", "terms", aggs_field, aggs_size) } rsp = self.es.search(body=body, index=ElasticConfig.krb5_ticket_index, doc_type=ElasticConfig.krb5_ticket_doc_type) if rsp: return rsp["aggregations"]["abc"]["buckets"] else: return [] @staticmethod def sec_tools_match(options: str) -> str: if options == "0x40800010": return "kekeo, Rubeus" elif options == "0x50800000": return "impacket" else: return "unknown"
def set_learning_end_time_setting(): value = move_n_days(datetime_utc_now_obj(), 10) logger.info("set learning end time: " + str(value)) name = "learning_end_time" redis = RedisHelper() mongo = MongoHelper(uri=MongoConfig.uri, db=MongoConfig.db, collection=MongoConfig.settings_collection) mongo.update_one(filter={"name": name}, doc={"$set": { "value": value }}, upsert=True) key = name + REDIS_KEY_SUFFIX redis.set_str_value(key, datetime_to_common_str(value))
class GetConfig(object): def __init__(self): self.redis = RedisHelper() def get_str(self, key) -> str: return self.redis.get_str_value(key) def get_int(self, key) -> int: return int(self.redis.get_str_value(key)) def get_dict(self, key) -> dict: return simplejson.loads(self.redis.get_str_value(key)) def get_obj(self, key): return simplejson.loads(self.redis.get_str_value(key)) def get_list(self, key) -> list: return self.redis.get_all_list(key)
def init_ldap_settings(domain, server, user, password): logger.info("init the ldap configuration.") if not server.startswith("ldap://"): server = "ldap://" + server mongo = MongoHelper(uri=MongoConfig.uri, db=MongoConfig.db, collection=MongoConfig.settings_collection) query = {"name": "ldap"} doc = { domain: { "server": server, "user": user, "password": password, "dn": get_dn_domain_name(domain) } } mongo.update_one(filter=query, doc={"$set": {"value": doc}}, upsert=True) redis = RedisHelper() redis.set_str_value("ldap" + REDIS_KEY_SUFFIX, simplejson.dumps(doc))
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"])
def get_all_dc_names(domain: str): """ 将DC列表入库 """ domain = get_netbios_domain(domain) logger.info("Search all domain controllers using LDAP.") dc_name_list = [] ldap_search = LDAPSearch(domain) dc_list = ldap_search.search_domain_controller() for each in dc_list: dc_name = str(each["cn"]) dc_name_list.append(dc_name) mongo = MongoHelper(MongoConfig.uri, MongoConfig.db, MongoConfig.settings_collection) doc = {domain: dc_name_list} logger.info(",".join(dc_name_list)) logger.info( "domain controller count: {count}".format(count=len(dc_name_list))) logger.info("Save all domain controllers to settings.") mongo.update_one({"name": "dc_name_list"}, {"$set": {"value": doc}}, True) redis = RedisHelper() redis.set_str_value("dc_name_list" + REDIS_KEY_SUFFIX, simplejson.dumps(doc))
def __init__(self): self.redis = RedisHelper()
def __init__(self): self.redis = RedisHelper() self.account_history = AccountHistory() self.es = ElasticHelper()
class AccountInfo(object): def __init__(self): self.redis = RedisHelper() self.account_history = AccountHistory() self.es = ElasticHelper() 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 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_user_by_name(self, user: str, domain: str) -> bool: """ 检查目标账号是否为 OU=Users """ key = user + REDIS_KEY_USERNAME_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_name(user=user, attributes=["cn"]) if user_entry: dn = str(user_entry.entry_dn) if "OU=Users".lower() in dn.lower() or "CN=Users".lower( ) in dn.lower(): 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 get_user_info_by_name(self, user_name: str, domain: str) -> User: key = user_name + REDIS_KEY_USERNAME_SID_SUFFIX # 先查redis user_sid = self.redis.get_str_value(key) # redis 缓存未命中 再查mongo if not user_sid: ldap = LDAPSearch(domain) user_entry = ldap.search_by_name(user_name, attributes=["objectSid"]) if not user_entry: return user_sid = user_entry.entry_attributes_as_dict["objectSid"][0] self.redis.set_str_value(key, user_sid, expire=ACCOUNT_INFO_REDIS_EXPIRE_TIME) user = User({ "user_name": user_name, "user_sid": user_sid, "logon_id": "", "domain_name": domain }) return user 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 check_target_is_aes_support(self, name: str, domain: str) -> bool: key = name + REDIS_KEY_USERNAME_AES_SUPPORT_SUFFIX # 先查redis is_support = self.redis.get_str_value(key) # if is_support is not None: return is_support == "true" else: ldap = LDAPSearch(domain) user_entry = ldap.search_by_name( name, attributes=["msDS-SupportedEncryptionTypes"]) if not user_entry: return False support_types = user_entry.entry_attributes_as_dict[ "msDS-SupportedEncryptionTypes"] if len(support_types) == 0: return False support_types = support_types[0] # 等于8 支持AES128加密 if support_types >= 8: self.redis.set_str_value(key, "true") return True else: self.redis.set_str_value(key, "false") return False 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 computer_is_sensitive_by_name(self, name: str, domain: str) -> bool: """ 检查某个计算机是否为敏感 1. 域控服务器 2. 自定义敏感主机 """ domain = get_netbios_domain(domain) # 域控服务器 if name.upper() in main_config.dc_name_list[domain]: return True # 敏感计算机 sensitive_computers = list( map(lambda x: x["name"], main_config.sensitive_computers)) if name.upper() in sensitive_computers: return True return False
def __init__(self): self.es = ElasticHelper() self.redis = RedisHelper()
class AccountHistory(object): def __init__(self): self.es = ElasticHelper() self.redis = RedisHelper() def set_workstation_by_ip(self, ip: str, workstation: str): key = ip + REDIS_KEY_LAST_WORKSTATION_IP_SUFFIX self.redis.set_str_value(key, workstation, expire=60 * 60 * 24) def set_ip_by_workstation(self, ip: str, workstation: str): key = workstation + REDIS_KEY_LAST_IP_WORKSTATION_SUFFIX self.redis.set_str_value(key, ip, expire=60 * 60 * 24) def get_last_workstation_by_ip(self, ip: str) -> str: key = ip + REDIS_KEY_LAST_WORKSTATION_IP_SUFFIX workstation = self.redis.get_str_value(key) if workstation: return workstation else: return self.search_last_workstation_by_ip(ip) def get_last_ip_by_workstation(self, workstation: str) -> str: key = workstation + REDIS_KEY_LAST_IP_WORKSTATION_SUFFIX ip = self.redis.get_str_value(key) if ip: return ip else: return self.search_last_ip_by_workstation(workstation) def search_last_workstation_by_ip(self, ip: str) -> str: query = { "query": get_must_statement( get_term_statement("event_id", 4768), get_wildcard_statement("event_data.TargetUserName.keyword", "*$"), get_term_statement("event_data.IpAddress.keyword", "::ffff:" + ip), get_term_statement("event_data.Status.keyword", "0x0"), ), "_source": ["event_data.TargetUserName", "event_data.TargetDomainName"], "size": 1, "sort": { "@timestamp": "desc" } } rsp = self.es.search(body=query, index=ElasticConfig.event_log_index, doc_type=ElasticConfig.event_log_doc_type) if rsp and rsp["hits"]["total"] > 0: data = rsp["hits"]["hits"][0]["_source"] workstation = data["event_data"]["TargetUserName"] return workstation[:-1] else: return "unknown" def search_last_ip_by_workstation(self, workstation: str) -> str: query = { "query": get_must_statement( get_term_statement("event_id", 4768), get_wildcard_statement("event_data.TargetUserName.keyword", workstation + "$"), get_term_statement("event_data.Status.keyword", "0x0"), get_must_statement( get_must_not_statement( get_term_statement("event_data.IpAddress.keyword", "::1")), get_must_not_statement( get_term_statement("event_data.IpAddress.keyword", "::ffff:127.0.0.1")))), "_source": ["event_data.IpAddress"], "size": 1, "sort": { "@timestamp": "desc" } } rsp = self.es.search(body=query, index=ElasticConfig.event_log_index, doc_type=ElasticConfig.event_log_doc_type) if rsp and rsp["hits"]["total"] > 0: data = rsp["hits"]["hits"][0]["_source"] ip = data["event_data"]["IpAddress"] return ip.replace("::ffff:", "") else: return "unknown"
def init_default_settings(domain): logger.info("init other settings.") redis = RedisHelper() mongo = MongoHelper(uri=MongoConfig.uri, db=MongoConfig.db, collection=MongoConfig.settings_collection) for name, value in default_settings.items(): if name == "domain_list": value = [domain] mongo.update_one(filter={"name": name}, doc={"$set": { "value": value }}, upsert=True) key = name + REDIS_KEY_SUFFIX if name in [ "domain_list", "VPN_ip_part", "detail_file_share_white_list_setting" ]: if len(value) != 0: redis.set_list(key, *value) elif name in [ "raw_data_expire", "honeypot_account", "alarms_merge", "sensitive_entry", "kerberos" ]: redis.set_str_value(key, simplejson.dumps(value)) elif name in ["brute_force_max"]: redis.set_str_value(key, str(value)) elif isinstance(value, list): if len(value) > 0 and isinstance(value[0], dict): redis.set_str_value(key, simplejson.dumps(value)) else: if len(value) != 0: redis.set_list(key, *value) elif isinstance(value, str): redis.set_str_value(key, value) elif isinstance(value, dict): redis.set_str_value(key, simplejson.dumps(value)) elif isinstance(value, int): redis.set_str_value(key, str(value))