コード例 #1
0
 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
コード例 #2
0
    def ntds_settings_delete(self, log: Log):
        """
            event_id 5141

            配置名称空间内设置删除,目标非域控计算机
        """
        # 目标服务器为已知的域控计算机名 则忽略
        patt = re.compile("^CN=NTDS Settings,CN=(.+?),.+", re.I)
        target_computer_name = patt.findall(log.object_info.dn)
        if not target_computer_name:
            return
        target_computer_name = target_computer_name[0]
        target_domain = log.event_data["DSName"]
        if target_computer_name in main_config.dc_name_list[get_netbios_domain(
                target_domain)]:
            return

        rule_list = [
            "CN=NTDS Settings", "CN=Servers", "CN=Default-First-Site-Name",
            "CN=Sites", "CN=Configuration"
        ]
        if log.object_info.class_ == "nTDSDSA":
            for rule in rule_list:
                if rule.lower() not in log.object_info.dn.lower():
                    return
            # 全部命中 则告警
            return {"alert_rule": SETTINGS_DELETE}
コード例 #3
0
    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()
コード例 #4
0
ファイル: DCShadow.py プロジェクト: cissyChen0/WatchAD-1
    def fake_dc_server_delete(self, log: Log):
        """
            event_id 5141

            配置名称空间内服务删除,目标非域控计算机
        """
        # 目标服务器为已知的域控计算机名 则忽略
        patt = re.compile("^CN=(.+?),.+", re.I)
        target_computer_name = patt.findall(log.object_info.dn)
        if not target_computer_name:
            return
        target_computer_name = target_computer_name[0]
        target_domain = get_netbios_domain(log.event_data["DSName"])
        if target_domain not in main_config.dc_name_list or target_computer_name in main_config.dc_name_list[target_domain]:
            return

        rule_list = ["CN=Servers", "CN=Default-First-Site-Name", "CN=Sites", "CN=Configuration"]
        if log.object_info.class_ == "server":
            for rule in rule_list:
                if rule.lower() not in log.object_info.dn.lower():
                    return
            # 四个规则全部命中 则告警
            return {
                "alert_rule": SERVER_DELETE
            }
コード例 #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
ファイル: LDAPSearch.py プロジェクト: zzxzshiyf/WatchAD
 def __init__(self, domain):
     self.domain = get_netbios_domain(domain)
     self.con = Connection(
         self._get_server(),
         user=main_config.ldap_account[self.domain]["user"],
         password=main_config.ldap_account[self.domain]["password"],
         auto_bind=True)
     self.domain_dn = main_config.ldap_account[self.domain]["dn"]
コード例 #7
0
    def scan(self):
        domain_list = main_config.domain_list
        dc_name_list_map = main_config.dc_name_list

        # 对所有的域进行检查
        for domain in domain_list:
            # 对所有的域控进行检查
            account = self._get_support_aes_account(domain)
            for dc_name in dc_name_list_map[get_netbios_domain(domain)]:
                self.check(domain, dc_name, account)
コード例 #8
0
ファイル: NTLMRelay.py プロジェクト: cissyChen0/WatchAD-1
    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)
コード例 #9
0
    def save_activity(self, domain, user_name, sid, dc_name, timestamp,
                      data: dict):
        doc = {
            "domain": get_netbios_domain(domain),
            "user_name": user_name,
            "sid": sid,
            "activity_type": self.activity_type,
            "dc_name": dc_name,
            "@timestamp": timestamp,
            "data": data
        }

        index = ElasticConfig.user_activity_write_index_prefix + datetime_to_log_date(
            datetime_now_obj())

        self.es.delay_index(body=doc,
                            index=index,
                            doc_type=ElasticConfig.user_activity_doc_type)
コード例 #10
0
ファイル: AccountInfo.py プロジェクト: zx273983653/WatchAD
    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
コード例 #11
0
ファイル: init_settings.py プロジェクト: cissyChen0/WatchAD-1
def init_ldap_settings(domain, server, user, password):
    netbios_domain = get_netbios_domain(domain)
    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 = {
        netbios_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))
コード例 #12
0
ファイル: skeleton_key_scan.py プロジェクト: zzdx713/WatchAD
 def check(self, domain, dc_name, account):
     nonce = getrandbits(31)
     current_time = time()
     etype = AES256
     as_req = build_as_req(get_netbios_domain(domain), account, None, current_time, nonce, True, etype)
     kdc_dns = "{dc_name}.{domain}".format(dc_name=dc_name, domain=domain)
     try:
         sock = send_req(as_req, kdc_dns, )
         data = recv_rep(sock)
         err_enc = decode(data, asn1Spec=KrbError())[0]
         print(err_enc['error-code'])
         if err_enc['error-code'] == KDC_ERR_ETYPE_NOSUPP:
             # TODO 发现万能钥匙
             return
     except timeout:
         pass
     except ConnectionRefusedError:
         pass
     except Exception as e:
         traceback.print_exc()
コード例 #13
0
    def run(self, log: Log):
        if "AllowedToDelegateTo" not in log.event_data:
            return

        if log.event_data["AllowedToDelegateTo"] == "-":
            return

        allowed_to_list = _parse_to_list(log.event_data["AllowedToDelegateTo"])

        netbios_name = get_netbios_domain(log.target_info.domain_name)
        record = self.delegation.find_constrained_delegation_by_sid(
            log.target_info.sid)

        # 更新记录
        if record and record["delegation_type"] == CONSTRAINED_DELEGATION:
            if record["allowed_to"] == allowed_to_list:
                return
            else:
                self.delegation.update_delegation(
                    sid=log.target_info.sid,
                    delegation_type=CONSTRAINED_DELEGATION,
                    allowed_to=allowed_to_list)
        else:
            self.delegation.new_delegation_record(
                user=User(log.target_info.__dict__),
                delegation_type=CONSTRAINED_DELEGATION,
                allowed_to=allowed_to_list)

        new_delegation_list = []
        for dele in allowed_to_list:
            if dele not in record["allowed_to"]:
                new_delegation_list.append(dele)

        # 查找新增高危约束委派
        high_risk_spn = self._check_high_risk_spn(new_delegation_list,
                                                  netbios_name)
        if len(high_risk_spn) == 0:
            return
        return self._generate_alert_doc(
            allowed_to_delegate_to=allowed_to_list,
            new_delegation_list=new_delegation_list)
コード例 #14
0
ファイル: DCShadow.py プロジェクト: cissyChen0/WatchAD-1
    def replication_monitoring(self, log: Log):
        """
            event_id  4928

            源命名上下文创建,发起来源非域控计算机
        """
        patt = re.compile("^CN=NTDS Settings,CN=(.+?),.+", re.I)
        source_computer = patt.findall(log.event_data["SourceDRA"])
        source_domain = get_domain_from_dn(log.event_data["SourceDRA"])
        if not source_computer:
            return
        source_computer = source_computer[0]
        netbios_domain = get_netbios_domain(source_domain)
        if netbios_domain not in main_config.dc_name_list or source_computer in main_config.dc_name_list[netbios_domain]:
            return

        # 如果当前的源地址不在已知的DC列表中,则告警
        return {
            "alert_rule": REPLICATION_MONITORING,
            "source_workstation": source_computer
        }
コード例 #15
0
ファイル: DCShadow.py プロジェクト: cissyChen0/WatchAD-1
    def spn_modify(self, log: Log):
        """
            event_id  4742
            非域控计算机修改SPN为异常值
        """
        # 目标服务器为已知的域控计算机名 则忽略
        target_computer_name = log.target_info.user_name[:-1]
        target_domain = get_netbios_domain(log.target_info.domain_name)
        if target_domain not in main_config.dc_name_list or target_computer_name in main_config.dc_name_list[target_domain]:
            return

        spn_list = log.event_data["ServicePrincipalNames"].split("\n\t\t")
        for spn in spn_list:
            if spn.startswith("GC/"):
                return {
                    "alert_rule": SPN_MODIFY,
                    "modify_computer": target_computer_name
                }
            if spn.startswith("E3514235-4B06-11D1-AB04-00C04FC2DCD2/"):
                return {
                    "alert_rule": SPN_MODIFY,
                    "modify_computer": target_computer_name
                }
コード例 #16
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))
コード例 #17
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"])