Evidence before alerts.
Local event correlation and explainable detection
Profile · All projects · Issues
Architecture · Benchmarks · Tuning · Threat model · Roadmap · Compatibility · Releasing · Security · Support · Change risk · Failure modes · Performance
[Threat model](docs/THREAT_MODEL.md)
Quick start · Rules · Samples · Tests
我挺喜欢那种“看起来没问题,但就是哪里不对劲”的日志。
这是我自己折腾的一个本地安全事件关联与行为检测引擎,主要用来研究登录行为、网络连接、周期外联、DNS 异常这些东西。
我不太想一上来就堆一整套 SIEM,也不想为了“看起来高级”先做一堆仪表盘。
先把数据喂进去,把时间窗口、行为关系、证据链和误报想明白,再谈界面。
登录失败 ─────┐
端口高扇出 ────┼─> 事件关联 -> 告警 -> 实体风险 -> JSON / Markdown
周期外联 ──────┤
高熵 DNS ──────┘
| 规则 | 检测思路 | 核心信号 |
|---|---|---|
AUTH-SEQ-001 |
连续登录失败后短时间成功 | sequence correlation |
NET-SCAN-001 |
单源对单目标端口高扇出 | sliding window |
NET-BEACON-001 |
低抖动周期性外联 | interval CV |
DNS-TUNNEL-001 |
长、高熵 DNS Label | entropy heuristic |
每条告警都会保留时间窗口和触发证据。
我不太喜欢那种最后只告诉我一句:
suspicious = true
但不告诉我为什么可疑的检测器。
所以 NightWatch 里每个规则尽量都能回答三个问题:
- 哪个实体触发了;
- 在什么时间窗口里触发;
- 到底是哪组数据让它触发。
git clone https://github.com/LLR6/Cybersecurity-Detection-Engineering-Android-Automation-Learning-by-Building.git
cd Cybersecurity-Detection-Engineering-Android-Automation-Learning-by-Building
python -m venv .venv
pip install -e ".[dev]"
pytest -q
nightwatch samples/demo.jsonl --format md --out report.md示例 Beacon 告警:
{
"rule_id": "NET-BEACON-001",
"score": 82,
"entity": "10.10.7.12->203.0.113.42:443",
"evidence": {
"interval_mean": 30.0,
"interval_cv": 0.0,
"samples": 7
}
}这个例子里,连接平均每 30 秒出现一次,而且时间抖动几乎为 0。
单独看一条连接没什么,但把时间序列拉出来以后,味道就不太一样了。
很多真正有意思的行为,不会出现在某一行日志里。
比如:
失败
失败
失败
失败
失败
成功
单看最后那个“成功”完全正常。
但如果前面刚连续失败了很多次,这个成功就值得重新看一眼。
所以 NightWatch 现在很多逻辑都是围绕:
事件 + 时间窗口 + 前后关系
来做。
我希望以后看到一条告警,不需要重新打开源码,也能大概判断:
为什么响?
响得有没有道理?
下一步该查什么?
所以每条 Alert 都会带 evidence。
核心检测目前基本只吃 Python 标准库。
不是因为依赖越少越“高级”,而是我希望:
git clone
pip install
直接跑
别为了一个简单检测器先起半天环境。
高熵 DNS 不等于 DNS Tunnel。
周期连接也不等于 C2。
端口高扇出也不一定就是扫描。
这些东西都只是 Signal。
检测器的任务是把值得继续看的东西捞出来,不是替分析人员直接下结论。
目前的 Beacon 规则会计算连续连接之间的时间间隔:
30s
30s
29s
31s
30s
30s
然后计算均值和变异系数:
CV = 标准差 / 平均值
如果间隔足够稳定,而且样本数量达到阈值,就会形成告警。
目前只是比较基础的版本,后面我准备继续加:
- jitter 容忍;
- 分桶统计;
- 长时间基线;
- 同主机多目的关联;
- 工作时间 / 非工作时间差异。
现在会看:
Label 长度
字符分布
Shannon Entropy
完整域名长度
例如这种:
aZ8fK2mQ9xP7cV4nR6tY1uI3oL5sD0hJ.telemetry.example
不会因为“长得奇怪”就直接判恶意,而是把它作为中等风险信号。
后面想继续加:
- 子域唯一率;
- NXDOMAIN 比例;
- 请求频率;
- 单客户端域名基线;
- TXT 查询行为;
- 域名长度分布异常。
我不太喜欢把几个分数直接粗暴相加。
所以目前同一个实体上的多条告警会做一个简单风险融合:
risk = 1 - product(1 - score_i)
这样做的好处是:
弱信号叠加以后会变得更值得关注,但又不会随便两条告警就直接冲到几百分。
例如:
Beacon + DNS 异常 + 认证异常
比单独出现其中一个更值得看。
src/nightwatch/
├── models.py
├── detectors.py
├── engine.py
├── report.py
└── cli.py
tests/
samples/
docs/
.github/workflows/
detectors.py 只负责检测逻辑。
engine.py 做调度和风险聚合。
models.py 统一事件和告警结构。
report.py 负责输出。
我现在尽量不把东西全塞进一个文件里,不然后面一旦加规则,很快就会变成一坨。
仓库里有 pytest 测试,覆盖目前几类核心检测:
认证失败 -> 成功
端口扫描
周期 Beacon
高熵 DNS
风险聚合
GitHub Actions 每次提交都会自动:
安装
↓
跑测试
↓
执行 demo
↓
生成 Markdown 检测报告
↓
上传 artifact
比“我本地能跑”靠谱一点。
- Zeek 日志适配
- Suricata eve.json 适配
- PCAP -> Flow 特征提取
- 类 Sigma 的 Sequence Rule
- ATT&CK Technique 映射
- 主机行为画像
- Beacon Baseline
- Precision / Recall 测试集
- 多阶段攻击链关联
- 图关系展示
- SARIF 输出
- CI 安全门禁
我更想把这个项目慢慢做成:
能解释
能复现
能测试
能扩展
而不是“功能很多,但自己过一个月都看不懂”。
项目仅用于防御安全研究、实验环境、安全分析和合法授权场景。
这一版我不想只在自己定义的 JSONL 里玩了,所以加了两个 Adapter:
- Suricata EVE JSON
- Zeek TSV 日志
数据先统一成 Event,再交给 Detector。也就是说检测逻辑不用知道日志到底来自 Suricata 还是 Zeek。
Suricata / Zeek / JSONL -> Event -> Detector -> Correlation -> Alert -> Risk
可以直接跑:
nightwatch samples/eve.json --input-format suricata --stats
nightwatch samples/conn.log --input-format zeek --stats
另外加了 CHAIN-001。它会把同一来源的服务探测、连续认证失败和最终成功放进同一个 10 分钟窗口里看。
我比较喜欢这种规则,因为它不是在问“某一条日志像不像攻击”,而是在问“这一串行为连起来以后是不是更值得调查”。
现在只给证据比较明确的规则做映射:
- AUTH-SEQ-001 -> T1110
- NET-SCAN-001 -> T1046
- DNS-TUNNEL-001 -> T1071.004
- CHAIN-001 -> T1046 + T1110
Beacon 暂时没硬贴 Technique。只有周期性外联还不够说明具体协议或 C2 技术,我不想让标签比证据跑得快。
更详细的规则想法在 docs/DETECTION_NOTES.md。
测试状态以 GitHub Actions 当前主分支结果为准。
一堆 Alert 平铺出来其实还是挺吵的。
所以这版加了 Case Builder:共享 IP、时间上足够接近的告警会自动聚在一起,再统一算 Case Risk、规则集合、ATT&CK 覆盖和时间线。
例如:
NET-SCAN-001
+
CHAIN-001
↓
CASE-001
直接生成 Case 报告:
nightwatch samples/demo.jsonl --case-report cases.md --out report.md
Case 不是按规则名硬凑,而是看实体交集和时间距离。默认时间间隔 30 分钟,可以用 --case-gap 调。
现在 CI 每次都会同时产出 report.md 和 cases.md。
测试状态以 GitHub Actions 当前主分支结果为准。
检测工程真正难的不是“能不能响”,而是响了以后怎么解释、怎么聚合、哪些已知正常行为为什么被排除。
现在 CLI 已支持:
nightwatch samples/demo.jsonl \
--out report.md \
--case-report cases.md \
--case-gap 30 \
--suppressions samples/suppressions.json \
--suppressed-out suppressed.json \
--stats新增两条实际工作流:
- Case report:把共享实体、时间上足够接近的 Alert 聚合成 Case,输出风险、规则、ATT&CK 与时间线。
- Suppression audit:对已知正常行为使用 rule/entity glob 做窄匹配,同时把被抑制的告警、匹配模式和 reason 单独写入 JSON,避免“消失的告警”无法追溯。
调参与 suppression 原则见 docs/TUNING.md。
suppression 不是白名单越多越好。这里把 suppression 当成一种需要版本控制和定期复核的检测配置,而不是把误报直接删掉。
samples/demo.jsonl 现在配有 samples/demo.truth.json。CI 会执行:
python scripts/evaluate_sample.py \
samples/demo.jsonl \
samples/demo.truth.json \
--out evaluation.md \
--fail-on-regression评估按 rule_id + entity 精确匹配,输出 TP / FP / FN、Precision、Recall 和 F1。新增检测器或修改阈值后,如果固定样本出现漏报或额外告警,CI 会直接失败。
这不是生产环境准确率声明,而是一个规则回归门禁:保证代码演进不会悄悄破坏已有可验证行为。
Architecture · Tuning · Security · Contributing · Changelog · Release checklist · Evaluation schema
These files document the project's architecture, safety boundaries, reproducibility assumptions and release process.
Part of LR Lab · Security × AI × Android × Automation
Build things that are useful, inspectable, and reproducible.