JakeDeha/Wazuh-Detection-Lab
GitHub: JakeDeha/Wazuh-Detection-Lab
基于Wazuh的暴力破解检测实验室项目
Stars: 0 | Forks: 0
# Wazuh SIEM 家用实验室 — 定制暴力破解检测工程
一个动手实践的家用实验室项目:在 Windows + WSL2 上部署完整的 **Wazuh SIEM/XDR** 堆栈,连接 Windows 终端代理,并编写 **自定义三层检测规则** 来捕获、升级和标记来自暴力破解身份验证攻击的可能妥协。检测通过端到端对实时模拟攻击进行了验证。
## 目录
- [概述](#overview)
- [架构](#architecture)
- [Wazuh 如何处理事件](#how-wazuh-processes-an-event)
- [部署](#deployment)
- [检测工程](#detection-engineering)
- [我解决的问题](#the-problem-i-solved)
- [测试与验证](#testing--validation)
- [结果](#results)
- [故障排除快速参考](#troubleshooting-quick-reference)
- [展示的技能](#skills-demonstrated)
- [学到的经验](#lessons-learned)
- [未来工作](#future-work)
## 概述
这个项目的目标是超越使用 SIEM,而是从头开始 **构建一个 SIEM**,然后针对真实遥测编写检测逻辑。具体来说:
1. 部署一个基于 OpenSearch 的容器化 Wazuh 堆栈(管理器、索引器、仪表板)。
2. 将实时 Windows 终端注册为 Wazuh 代理。
3. 使用 XML 编写自定义检测规则以识别暴力破解登录活动。
4. 建立一个二级关联规则,当攻击 *持续* 时升级严重性,而不仅仅是单次爆发。
5. 通过模拟攻击验证整个管道,并确认仪表板中正确显示警报。
Wazuh 是一个开源安全平台(SIEM + XDR),在许多行业中用于生产。在我的基础设施上运行它——基于代理的收集、本地管理器和 OpenSearch 分析层——与真实的本地安全监控环境的操作模式非常相似。
## 架构
堆栈在 Windows 主机上作为四个 Docker 容器运行,使用 WSL2 后端。遥测从终端代理流向管理器,管理器根据规则集评估事件,并将警报发送到由 OpenSearch 支持的索引器进行存储、搜索和仪表板中的可视化。
```
┌──────────────────┐ ┌──────────────────────────────────────────┐
│ Windows Endpoint │ │ Docker (WSL2 backend) │
│ "Jake-laptop" │ │ │
│ │ 1514 │ ┌────────────┐ ┌──────────────────┐ │
│ Wazuh Agent ───┼────────┼─▶│ Wazuh │──▶│ Wazuh Indexer │ │
│ (Security log, │ 1515 │ │ Manager │ │ (OpenSearch) │ │
│ Sysmon, FIM) │ │ │ (analysisd)│ └────────┬─────────┘ │
│ │ │ └────────────┘ │ │
└──────────────────┘ │ ┌────────▼─────────┐ │
│ │ Wazuh Dashboard │ │
│ │ (OpenSearch Dash)│ │
│ └──────────────────┘ │
└──────────────────────────────────────────┘
│
https://localhost (443)
Analyst access
```
**组件:**
| 容器 | 镜像 | 角色 |
|-----------|-------|------|
| Wazuh 管理器 | `wazuh/wazuh-manager:4.9.2` | 接收代理遥测,运行解码器 + 规则 (`analysisd`),生成警报 |
| Wazuh 索引器 | `wazuh/wazuh-indexer:4.9.2` | 基于 OpenSearch 的索引和分析引擎;存储和使警报可搜索 |
| Wazuh 仪表板 | `wazuh/wazuh-dashboard:4.9.2` | OpenSearch Dashboards 前端,用于威胁狩猎和可视化 |
| Windows 代理 | Wazuh 代理 | 将 Windows 安全事件日志(包括事件 ID 4625)、文件完整性和 rootcheck 数据转发 |
## Wazuh 如何处理事件
了解内部管道对于编写实际触发的规则至关重要。每个代理发送的事件都通过管理器分析守护进程 (`wazuh-analysisd`) 的固定顺序的几个阶段。规则只能匹配早期阶段成功生成的字段——这正是我的第一个规则失败的地方(见 [我解决的问题](#the-problem-i-solved))。
```
Agent (Windows) Manager (wazuh-analysisd) Indexer / Dashboard
───────────── ───────────────────────── ───────────────────
Windows Security
Event Log (4625) ──┐
│ (1) PRE-DECODING extract timestamp, hostname, program
Sysmon / FIM / ├──────────▶ (2) DECODING map raw log → fields (srcip, srcuser, …)
rootcheck data ────┘ (3) RULE MATCHING evaluate ruleset, assign level + groups
(4) ALERTING if level ≥ threshold, emit alert ──────▶ store + index
↓
searchable in
Threat Hunting
```
四个阶段以及每个阶段为什么对这个项目很重要:
1. **预解码** — `analysisd` 从原始日志行中提取通用字段:时间戳、主机名和程序/源。对于 Windows 事件,这是通道和提供者;对于 syslog,是经典的 `Mon DD HH:MM:SS host program:` 标头。
2. **解码** — 一个 *解码器* 将事件体解析为命名字段,例如 `srcip`、`srcuser`、`data.win.system.eventID` 等。**关键的是,只有当解码器从事件中实际存在的数据中填充了字段时,字段才存在**。Windows 本地登录失败不携带网络源 IP,因此 `srcip` 简单地不存在——这是我的初始规则失败的根本原因。
3. **规则匹配** — 解码的事件与规则集(内置规则加上 `local_rules.xml` 中的任何规则)进行测试。规则可以在解码字段上匹配,从其他规则(`if_sid`、`if_matched_sid`)链出,并应用频率/时间关联。具有最高级别的第一个匹配规则获胜,并分配警报级别和合规性组。
4. **警报** — 如果匹配的规则的级别达到警报阈值,`analysisd` 将写入警报,然后将其发送到索引器(OpenSearch)并成为可搜索和可视化的仪表板威胁狩猎视图中的警报。
塑造规则设计的两个 Wazuh 习惯:
- **警报级别运行 0–15**。0 是信息性/忽略,5 是常规的单次身份验证失败,10 是“值得注意/可能攻击”,12+ 是“高优先级,立即调查”。选择 10 和 12 为两个级别,故意将其放置在该范围内。
- **自定义规则 ID 必须大于等于 100000**。低于该 ID 的 ID 保留用于捆绑的规则集(`0xxx`–`9xxxx` 范围内大约有 4,400+ 个规则)。使用 `100100`/`100101` 保证在 Wazuh 更新其签名时不会发生冲突。
## 部署
堆栈在 Windows 上使用 Docker Desktop 和 WSL2 后端部署。构建分为两个阶段:首先是一个独立的 OpenSearch 集群(直接学习索引/分析层),然后是集成的 Wazuh 堆栈。OpenSearch 需要足够的内存,并且具有特定的内核要求,因此在任何内容可靠运行之前,需要进行几个主机级别的调整步骤。
### 1. 调整 WSL2 资源
OpenSearch 需要足够的内存,在 WSL2 后端,RAM 由 Windows 通过 `.wslconfig`(位于 `C:\Users\\.wslconfig`)管理,而不是由 Docker Desktop 直接管理。
```
[wsl2]
memory=8GB
processors=4
swap=2GB
kernelCommandLine = "sysctl.vm.max_map_count=262144"
```
`vm.max_map_count=262144` 设置是 OpenSearch 所必需的——没有它,索引器无法启动。通过 `kernelCommandLine` 设置它使其持久化,跨 WSL 重启。编辑后,更改通过以下方式应用:
```
wsl --shutdown
```
…然后重启 Docker Desktop。
### 2. 第一阶段——独立的 OpenSearch(学习分析层)
为了了解最终支持 Wazuh 的 OpenSearch 层,我首先部署了一个独立的 OpenSearch + OpenSearch Dashboards 集群:
```
mkdir C:\opensearch
cd C:\opensearch
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/opensearch-project/opensearch-build/main/docker/release/dockercomposefiles/docker-compose-default.x.yml" -OutFile "docker-compose.yml"
$env:OPENSEARCH_INITIAL_ADMIN_PASSWORD=""
docker compose up
```
成功启动显示 `[+] up 19/19`,所有容器 **已创建**,集群健康从 **黄色** 转变为 **绿色**。OpenSearch Dashboards 然后在 `http://localhost:5601` 加载。此阶段确认了索引引擎、安全插件和仪表板在添加 Wazuh 之前都正常工作。
### 5. 访问仪表板
Wazuh 仪表板可通过 `https://localhost`(底层的 OpenSearch Dashboards 也运行在 `http://localhost:5601`)访问。从这里,可以访问代理状态、警报和威胁狩猎视图。
## 检测工程
自定义规则位于 **Wazuh 管理器** 的 `/var/ossec/etc/rules/local_rules.xml` 中。自定义规则 ID 必须大于等于 **100000** 以避免与 Wazuh 的内置规则集(包含约 4,400+ 个规则)发生冲突。
检测目标是 **三层** 设计:
- **第一层(级别 10)**:当在终端上检测到暴力破解爆发时触发。
- **第二层(级别 12)**:当这些爆发 *重复* 在一个短时间内时升级严重性——这是持续、活跃活动的标志,而不仅仅是单次爆发。
- **第三层(级别 13)**:标记 *可能的妥协* —— 当暴力破解爆发仍然活跃时发生的成功交互式登录。失败后跟随成功是攻击者最终猜出密码的标志。
### 规则集
```
5716
1.1.1.1
sshd: authentication failed from IP 1.1.1.1.
authentication_failed,pci_dss_10.2.4,pci_dss_10.2.5,
60204
Custom: Windows brute-force logon attempt detected on agent
authentication_failures,pci_dss_10.2.4,pci_dss_10.2.5,gdpr_IV_35.7.d,
100100
Custom: SUSTAINED Windows brute-force campaign - 3+ failure bursts in 5 minutes
authentication_failures,attack,pci_dss_10.2.4,pci_dss_10.2.5,gdpr_IV_35.7.d,
60118
100100
Custom: POSSIBLE COMPROMISE - successful interactive logon after brute-force burst
authentication_success,attack,pci_dss_10.2.5,gdpr_IV_35.7.d,
```
### 部署规则更改
规则集在此存储库中作为 `local_rules.xml` 进行版本控制。要将它部署到正在运行的管理器:
```
docker cp ./local_rules.xml :/var/ossec/etc/rules/local_rules.xml
docker exec -it /var/ossec/bin/wazuh-control restart
```
在容器内编辑的最佳方式是 here-doc,它避免了多行粘贴损坏 `analysisd` 在重启时的多行粘贴损坏:
```
docker exec -it bash
cat > /var/ossec/etc/rules/local_rules.xml << 'EOF'
... ruleset ...
EOF
/var/ossec/bin/wazuh-control restart
```
### 它是如何工作的
三个规则形成一个链:第一个检测单个爆发,第二个监视第一个的重复。
- **`60204 `** — 规则 100100 是一个 *依赖规则*:它仅在规则 60204 已经匹配时评估。60204 是 Wazuh 的内置“多个 Windows 登录失败”签名,它本身在从 Windows 安全通道解码的事件 ID 4625 上触发。通过使用 `if_sid` 链接,规则 100100 继承了 60204 的正确的 Windows 特定关联,而不是重新实现它——它只是将命中重新分类为自定义、合规性标记的警报,级别为 10。这是规则集中最重要的设计决策,也是使检测工作正常的关键修复。
- **`100100 `** — 规则 100101 是一个 *复合/关联规则*。与 `if_sid`(依赖于直接父事件)不同,`if_matched_sid` 在时间窗口内向后查看规则 100100 触发的频率。这是将一系列单个爆发转换为单个更高阶的“活动”检测的原因。
- **`frequency="3" timeframe="300"`** — 升级仅在 100100 在滚动 300 秒窗口内匹配 **3 次或更多次** 时触发。`analysisd` 维护一个内存事件缓存来计数这些匹配;计数器在事件从窗口中老化时重置。一次失败的登录是背景噪音(一个粗心的密码),五分钟内三次爆发是有意、持续的尝试——一个有意义的区分行为,而不仅仅是更高的计数。
- **`60118 `(第三层)** — 规则 100102 仅在 **成功的交互式登录** 上评估。60118 是 Wazuh 的本地“Windows 工作站登录成功”规则,是 60106 的子规则,它特别需要 `logonType: 2`(交互式)。通过链接到 60118 而不是更广泛的 60106,将检测精确到由手动键盘或 `runas` 风格的暴力破解产生的确切事件,从服务(类型 5)和网络(类型 3)登录中删除噪音。
- **`100100 `(第三层门)** — 成功的登录通常是良性的(本地级别 0–3)。它仅在 tier-1 规则 100100 在 `timeframe` 窗口内触发时升级到级别 13——即失败后跟随成功。如果没有最近的失败,正常的登录不会产生警报。注意第三层使用 **没有 `frequency` 属性**:Wazuh 需要 `frequency` >= 2,因此“在单个相关事件上触发”通过 `if_matched_sid` + `timeframe` 单独表达。
- **严重性级别** — 爆发为 10 级,持续活动为 12 级。跳跃编码了分类优先级:级别 10 的警报值得审查,带有 `attack` 组的级别 12 警报需要立即调查。分析师和下游自动化(例如警报集成、主动响应)可以过滤级别以避免在低价值事件中淹没。
- **`` 标记**——除了分类(`authentication_failures`、`attack`)之外,组字段还携带 **合规性映射**:`pci_dss_10.2.4` / `pci_dss_10.2.5`(失败的访问尝试的审计跟踪)和 `gdpr_IV_35.7.d`(处理的安全性)。在真实的 SOC 中,这些标记允许从同一个检测为安全分类和合规性报告提供数据——这就是为什么我包括它们而不是让规则未标记的原因。
**`if_sid` 与 `if_matched_sid` 的区别**:`if_sid` 将规则链接到 *单个触发事件*(父匹配触发一次)。`if_matched_sid` 将规则链接到 *另一规则在时间内的频率*(当计数阈值被跨越时触发一次)。第一层使用前者重新分类每个爆发;第二层使用后者检测爆发模式。
在编辑后,通过重启管理器加载规则:
```
/var/ossec/bin/wazuh-control restart
```
两个自定义规则都干净地加载并出现在仪表板的 **管理 → 规则 → 自定义规则** 视图中。

*`wazuh-control restart` 清洁地重新加载管理器(左),以及仪表板确认本地规则 60204——第一层链接的父规则,在 `Jake-laptop` 上以级别 10 触发。*
## 我解决的问题
检测规则的第一个版本 **没有触发**——并且调试 *为什么* 是这个项目最有价值的部分。
**初始方法**:我编写了一个规则来计算来自同一源 IP 的身份验证失败,使用 ` ` 与频率/时间框架阈值一起。
**失败**:即使重复失败的登录,也没有触发警报。解码正在工作(基本事件被正确解析),但关联规则从未触发。
**根本原因**:失败的 **本地 Windows 登录**(事件 ID 4625,由 `runas` 等工具生成)通常不携带源 IP,就像远程 SSH 失败一样。规则正在等待通过一个在遥测中不存在的字段(`srcip`)来分组事件,因此关联计数器从未增加,规则从未达到频率阈值。规则中内置的假设与实际数据形状不符。
我通过直接检查解码的事件来确认这一点。远程 SSH 失败解码为包含填充的 `srcip`:
```
**Phase 2: Completed decoding.
name: 'sshd'
srcip: '203.0.113.40' ← present for network logons
srcuser: 'admin'
```
…但是本地 Windows `runas` 失败(事件 ID 4625,登录类型 2)解码 **没有** `srcip` 字段——对于本地交互式登录没有网络源。在规则中使用 ` `,`analysisd` 没有可以分组的值,因此关联计数器从未增加,规则从未达到频率阈值。解码器工作得很好;我的规则只是基于一个对于此事件类型不存在的字段。
**修复**:而不是尝试重新计数原始失败并按不存在的 IP 组合,我:
1. **链接到 Wazuh 的本地 Windows 规则(60204)**,该规则已经使用 Windows 特定逻辑正确关联了 Windows 登录失败——`60204 `。60204 不依赖于 `srcip`,因此它正确地为本地登录触发。
2. **从升级规则中删除 ` `**,因为本地 Windows 事件无法可靠地填充它,并且相反,仅通过 `if_matched_sid` 通过(已经关联的)第一层警报的重复来升级。
这将一个损坏的规则变成了一个工作的两层检测——并且加强了一个核心检测工程教训:**你必须了解你的数据实际上是什么样子,而不是你假设它是什么样子**。对于 SSH (`same_source_ip`) 工作良好的逻辑对于本地 Windows 登录静默失败,并且只有检查解码的事件才能揭示原因。
## 测试与验证
### 语法验证
在重启管理器之前,使用 `wazuh-logtest` 验证了规则集的语法。在“每行一个日志”提示下干净启动确认了完整的规则集(包括自定义规则)已加载而没有 XML 错误。
```
/var/ossec/bin/wazuh-logtest
```
### 实际攻击模拟
在规则处于活动状态后,通过从真实的 Windows 终端生成失败的登录尝试(事件 ID 4625)来在 Windows 代理上模拟暴力破解活动:
```
runas /user:FakeUser123 cmd
```
重复使用错误的密码生成失败的登录遥测。要触发 **第二层升级**,在 5 分钟窗口内生成 **三个爆发**,使规则 100100 触发三次并汇总到规则 100101。

*Windows 终端已注册并报告:代理 `Jake-laptop`(ID 001)、Microsoft Windows 11、**活动**、运行代理 v4.9.2——检测规则消耗的事件 ID 4625 遥测的来源。*
### 威胁狩猎验证
结果在仪表板的 **威胁狩猎 → 事件** 视图中通过过滤规则 ID 进行了验证:
```
rule.id: 100100 → tier-1 burst detections (level 10)
rule.id: 100101 → tier-2 sustained-campaign escalation (level 12)
```

*威胁狩猎 → 事件表显示来自 `Jake-laptop` 的实时解码事件,使用 `agent.name`、`rule.description`、`rule.level` 和 `rule.id` 列来确认确切触发了哪些规则。*

*第一层(规则 100100,级别 10):在 `Jake-laptop` 上过滤威胁狩猎仪表板以 `rule.id: 100100` 显示爆发检测——总共有 5 个,零个在级别
标签:Docker, NIDS, PoC, Redis利用, Wazuh, WSL, WSL2, XML, 事件处理, 安全实验室, 安全技能, 安全架构, 安全测试, 安全防御评估, 容器化, 攻击性安全, 暴力破解, 端点安全, 红队行动, 自定义规则, 补丁管理, 请求拦截