コード例 #1
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
 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
コード例 #2
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
 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
コード例 #3
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
 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
コード例 #4
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
 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
コード例 #5
0
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))
コード例 #6
0
    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
コード例 #7
0
    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)
コード例 #8
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
 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
コード例 #9
0
def get_all_unconstrained_delegation_users(domain: str):
    """
        获取所有 无约束委派 权限的用户
    """
    d = Delegation()
    ldap_search = LDAPSearch(domain)
    entries = ldap_search.search_unconstrained_accounts()
    if entries:
        for each in entries:
            assert isinstance(each, Entry)

            user = User({
                "user_name":
                each.entry_attributes_as_dict["sAMAccountName"][0],
                "user_sid":
                each.entry_attributes_as_dict["objectSid"][0],
                "domain_name":
                domain
            })
            d.new_delegation_record(user=user,
                                    delegation_type=UNCONSTRAINED_DELEGATION)
コード例 #10
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
    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
コード例 #11
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
 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
コード例 #12
0
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))
コード例 #13
0
 def _get_group_name(self, sid, domain):
     ldap = LDAPSearch(domain)
     entry = ldap.search_by_sid(sid, attributes=["cn"])
     if entry:
         return str(entry["cn"])
コード例 #14
0
    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"])
コード例 #15
0
 def _get_support_aes_account(self, domain):
     ldap = LDAPSearch(domain)
     entry = ldap.get_support_aes_account()
     if entry:
         return str(entry["sAMAccountName"])