Skip to content
This repository has been archived by the owner on Jul 13, 2023. It is now read-only.

Development(中文)

9ian1i edited this page Oct 31, 2019 · 1 revision

项目结构说明

WatchAD  		    根目录
├─libs                      引用的部分外部库
├─models                    封装的数据对象
├─modules                   主模块目录
│  ├─alert        	    告警处理的相关代码
│  ├─detect        	    威胁检测代码
│  │  ├─event_log           基于事件日志的检测代码
│  │  │  ├─ ... ...         分类的检测代码
│  │  │  └─record           用于记录域内实体的各种活动,不告警
│  │  └─traffic_kerberos    基于kerberos流量的检测代码(本次不开源,移除)
│  └─record_handle          分析时用到的其它信息操作文件
├─scripts                   安装、定时任务等用到的脚本
├─settings                  各种配置文件,获取配置信息的操作文件
├─tools                     工具函数
├─start.py                  检测引擎启动入口代码
├─WatchAD.py                项目主程序入口代码,可进行安装、启动、停止等操作
└─supervisor.conf           supervisor的配置文件,WatchAD使用它托管进程

自定义开发检测模块

WatchAD支持自定义编写添加检测模块,下面用 敏感用户组修改 作为示例来讲解如何编写自定义模块。

1. 新建文件

在目录 {project_home}/modules/detect/event_log下,按照威胁分类为了六个目录,record目录是用于记录域内实体的相关活动,和告警无关,暂时不用关注。

我们根据当前威胁类别,敏感用户组修改一般用于权限维持添加控制的账户,所以我们在 {project_home}/modules/detect/event_log/persistence目录下新建文件 ModifySensitiveGroup.py

2. 创建检测类

新建了文件之后,首先我们定义一下当前模块需要分析的事件日志ID列表威胁类别代号标题简要描述模板。通过测试环境本地复现以及查阅相关文档 ,我们知道往安全组中添加用户会触发 472847324756三种事件,分别对应不同的范围。威胁类别代号和现有的不重复,且按照分类来定义,比如权限维持是以5开头。简要描述模板用于大致说明威胁活动的情况,其中用[]包裹起来的字段名对应后面的告警内容字段,在Web平台显示时会自动替换。

EVENT_ID = [4728, 4732, 4756]

ALERT_CODE = "506"
TITLE = "Modification of sensitive groups"
DESC_TEMPLATE = "来自于 [source_ip]([source_workstation]) 使用身份 [source_user_name] 将目标用户 [target_user_name] 添加到了敏感组 [group_name] 中。"

接下来引入DetectBase类,并创建一个和文件名相同的类,继承DetectBase,实现 _generate_alert_doc_get_level 方法。

from settings.config import main_config
from models.Log import Log
from modules.detect.DetectBase import DetectBase, HIGH_LEVEL
from tools.common.common import get_cn_from_dn

EVENT_ID = [4728, 4732, 4756]

ALERT_CODE = "506"
TITLE = "Modification of sensitive groups"
DESC_TEMPLATE = "来自于 [source_ip]([source_workstation]) 使用身份 [source_user_name] 将目标用户 [target_user_name] 添加到了敏感组 [group_name] 中。"


class ModifySensitiveGroup(DetectBase):
    def __init__(self):
        super().__init__(code=ALERT_CODE, title=TITLE, desc=DESC_TEMPLATE)

    def run(self, log: Log):
        # 初始化检测模块,必需
        self.init(log=log)

        group_name = log.target_info.user_name
		
        # 动态配置的敏感组列表
        sensitive_groups = list(map(lambda x: x["name"], main_config.sensitive_groups))
        # 如果修改的组存在于敏感组中,则告警
        if group_name in sensitive_groups:
            return self._generate_alert_doc()

    def _generate_alert_doc(self, **kwargs) -> dict:
        # 通过登录名和登录ID查找登录时的IP
        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,
            "group_name": self.log.target_info.user_name,
            "target_user_name": get_cn_from_dn(self.log.event_data["MemberName"]),
            # 以下字段内容可选
            ... ...
        }
        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
    

简单解释一下上面的代码,

  • models.Log :这是引擎简单封装的日志对象,将字典对象变成对象属性来访问,减少拼写错误,具体可查看Log类的代码内容。

  • _get_level:顾名思义是返回当前威胁活动的危害等级,_generate_alert_doc 是返回告警内容的文档。

  • _generate_alert_doc的内容比较固定:

    • form_data :用于自定义保存当前威胁活动的相关信息,比如 来源IP(source_ip),来源主机名(source_workstation),来源用户名(source_user_name),目标用户名(target_user_name)等等,根据每种威胁活动会有区别,但表达相同含义的字段名必须一致,比如你不能填来源用户名的字段名为:source_user。但有一些内容是必填的,可以通过以下思路去决定填写哪些字段:”哪个来源IP和主机,谁,做了什么,目标是谁。“,具体可参考其它检测模块的写法。
    • unique_id: 用于合并重复的告警,一般是威胁活动代号 + 来源用户名或来源IP
    • level: 危害等级
  • run: 运行的主入口,参数log是当前需要分析日志,因为我们指定了 4728, 4732, 4756三种日志,所以这里出现的日志也只会有这三种类型。

    • self.init(log=log):这行代码必须在该函数最开始的地方,用于初始化当前的检测模块环境,每一个新的日志,都会重新运行一遍run函数。
    • 返回值:如果没有任何异常,返回空即可。如果发现了威胁,返回 self._generate_alert_doc()告警文档,引擎会自动执行接下来的入库合并等操作。
Clone this wiki locally