recluseexe/soc-homelab
GitHub: recluseexe/soc-homelab
一个蓝队家庭实验室项目,通过模拟真实攻击并在 Splunk SIEM 中进行检测工程,帮助安全分析师练习威胁检测与事件响应。
Stars: 1 | Forks: 0
# soc-homelab
模拟真实攻击(暴力破解、Web 扫描、权限提升、网络入侵)并在 Splunk 中进行检测工程的 家庭实验室
# 蓝队家庭实验室 — SOC 检测工程作品集
这是一个自建的家庭实验室,用于模拟小型企业网络,旨在练习使用 Splunk 作为 SIEM 来检测真实的攻击技术。所有环境均使用 VirtualBox 在单台笔记本电脑上运行。
## 实验室架构
```
┌─────────────────────┐ ┌──────────────────────┐
│ Windows (Host) │◄────────│ Ubuntu Server VM │
│ Splunk Enterprise │ :9997 │ (Splunk Forwarder) │
│ SIEM + Dashboards │ │ SSH / Apache / │
│ │ │ auditd / Suricata │
└─────────────────────┘ └──────────────────────┘
▲ ▲
│ attacks against │
└─────────────┬───────────────────┘
│
┌─────────────────┐
│ Kali Linux VM │
│ (Attacker) │
│ Hydra, Nikto, │
│ Gobuster, nmap │
└─────────────────┘
```
这三台机器都位于同一个内部/桥接子网中。Ubuntu 虚拟机运行 **Splunk Universal Forwarder**,通过 **9997** 端口将日志发送到运行在 Windows 宿主机上的 Splunk Enterprise。每个项目都遵循相同的核心循环:
**攻击 (Kali) → 生成日志 (Ubuntu/Windows) → 转发至 Splunk → 使用 SPL 检测 → 响应/阻断**
## 核心概念:每个 SPL 查询是如何构建的
本实验室中的每个检测查询都遵循相同的五个阶段结构。如果你记住了这句话,几乎可以从头构建任何查询:
```
index=X "keyword" ← SEARCH: where to look, what raw text to match
| rex "pattern with (?Pcapture)" ← EXTRACT: pull structured fields out of raw text
| stats count as some_name by field ← AGGREGATE: group and count
| where some_name >= threshold ← FILTER: only keep what matters
| sort - some_name ← ORDER: highest/most recent first
```
### 你几乎会在每个查询中用到的命令
| 命令 | 作用 | 类比 |
|---|---|---|
| `index=` | 搜索哪个数据桶 | 选择哪个档案柜 |
| `sourcetype=` | 该索引下的哪种日志格式 | 选择哪个抽屉 |
| `"keyword"` | 在进行任何处理之前过滤原始文本 | 基础的 Ctrl+F |
| `\| rex "(?Ppattern)"` | 使用正则表达式从非结构化文本中提取新字段 | 高亮并标记句子的一部分 |
| `\| spath` | 从**结构化** JSON 中自动提取字段(无需正则表达式) | 打开电子表格而不是文本文件 |
| `\| stats count by field` | 按字段统计事件数 | Excel 数据透视表 |
| `\| where field >= N` | 过滤聚合后的结果 | SQL 的 `WHERE` 子句 |
| `\| sort - field` | 降序排列(`+` = 升序) | 对列进行排序 |
| `\| bucket _time span=1m` | 将事件分组到时间窗口中 | 将时间戳舍入到各个存储桶 |
| `\| table field1 field2` | 仅显示特定列 | 隐藏电子表格的列 |
| `\| head N` | 限制为前 N 行 | SQL 中的 LIMIT |
**`rex` 和 `spath` 之间的关键区别:** `rex` 用于纯文本日志(auth.log、Apache 访问日志),你必须手动描述模式以提取字段。`spath` 用于 JSON 日志(如 Suricata 的 `eve.json`),其中结构已经存在——Splunk 直接读取它,无需正则表达式。
## 项目 1 — SSH 暴力破解检测
**攻击面:** Linux SSH(端口 22)
**攻击者工具:** Hydra
**日志源:** `/var/log/auth.log` → `index=auth`, `sourcetype=linux_secure`
### 模拟内容
```
hydra -t 4 -V -f -l vboxuser -P /tmp/passlist.txt ssh://192.168.100.27
```
Hydra 尝试使用包含常见密码的小型字典针对真实的 SSH 账户进行攻击,在 `auth.log` 中产生了大量 `Failed password` 记录。
### 检测查询
```
index=auth sourcetype=linux_secure "Failed password"
| rex "Failed password for (?:invalid user )?(?P\S+) from (?P\S+)"
| stats count as failed_attempts by src_ip, user
| where failed_attempts >= 5
| sort - failed_attempts
```
**生效原因:** `auth.log` 会为每次失败的登录尝试写入一行,其中包含尝试的用户名和来源 IP。`rex` 将两者从原始文本中提取为真实的字段,`stats` 统计每个攻击者/用户名对的尝试次数,而 `where` 设定了区分“暴力破解”与正常输入错误的阈值。
### 响应
```
sudo ufw insert 1 deny from to any port 22 proto tcp
```
### 核心要点
这是最简单且最常见的 SOC 检测模式:**按来源统计失败的认证事件,超过阈值则告警。** 相同的模式也出现在项目 3 (RDP) 和项目 6 (Windows 登录) 中。
## 项目 2 — Web 服务器攻击检测
**攻击面:** Apache HTTP 服务器(端口 80)
**攻击者工具:** Nikto(漏洞扫描器)、Gobuster(目录暴破工具)、手动 `curl`
**日志源:** `/var/log/apache2/access.log` → `index=web`, `sourcetype=apache_access`
### 模拟内容
```
nikto -h http://192.168.100.27
gobuster dir -u http://192.168.100.27 -w /usr/share/wordlists/dirb/common.txt
```
Nikto 发起了数以千计的请求,探测已知的易受攻击路径,并将其 User-Agent 伪装成真实的浏览器(Chrome、Firefox、Safari)以混入其中——这是一个重要的教训:**仅依靠 User-Agent 字符串来检测扫描器是不可靠的。**
### 为什么第一种检测方法失败了
最初的计划是搜索 `useragent="*Nikto*"`——但由于 Nikto 将其 User-Agent 伪装成了合法的浏览器,这种方法什么也没发现。这是一个现实世界中的教训:**攻击者通过伪造标识字符串来逃避基于特征的检测。**
### 真正有效的检测 —— 基于行为而非特征
```
index=web sourcetype=apache_access
| rex field=_raw "(?P\d+\.\d+\.\d+\.\d+)"
| bucket _time span=1m
| stats count as requests by _time, src_ip
| where requests >= 100
| sort - requests
```
**生效原因:** 这不是试图识别*工具*,而是检测*行为*——没有合法用户会在一分钟内浏览 100 多个页面。基于速率的检测可以捕获任何扫描器,无论是否伪装,并且仅通过更改标头是无法规避的。
### 观察结果
一个攻击 IP 在不到 24 小时内产生了 **19,946 次请求**,单分钟内的爆发量超过 7,000 次请求——与正常的一位数浏览器流量基线相比,这是一个不容忽视的显著特征。
### 核心要点
当攻击者可以控制其流量声明的身份时,**基于行为/体量的检测要胜过特征匹配。** 这是蓝队的核心原则——检测*它做了什么*,而不仅仅是*它声称自己是什么*。
## 项目 3 — RDP 暴力破解检测
**攻击面:** Windows 远程桌面(端口 3389)
**攻击者工具:** Crowbar(失败 —— 被 NLA 阻断),随后使用 Hydra(成功)
**日志源:** Windows 安全事件日志 → `index=main`, `sourcetype="WinEventLog:Security"`
### 模拟内容
```
hydra -t 4 -V -f -l administrator -P /tmp/rdppass.txt rdp://192.168.100.65
```
### 真实的排错教训:工具选择很重要
Crowbar 在针对目标时悄无声息地失败了——真正的阻碍是**网络级别身份验证 (NLA)**,这是一项 Windows 安全功能,要求在建立完整的 RDP 会话*之前*进行身份验证,而 Crowbar 的实现无法完成协商。Hydra 的 RDP 模块(标记为实验性)处理了它。**教训:并非每个暴力破解工具都以相同的方式实现每个协议——如果某个工具失败了,在假设目标不可达之前,要了解*为什么*。**
### 检测查询
```
index=main sourcetype="WinEventLog:Security" EventCode=4625
| rex field=_raw "Source Network Address:\s+(?P\d+\.\d+\.\d+\.\d+)"
| stats count as failed_attempts by src_ip, Account_Name
| where failed_attempts >= 3
| sort - failed_attempts
```
**生效原因:** Windows 会将每次失败的登录记录为 **Event ID 4625**,无论它是通过 RDP、本地控制台还是网络共享发生的。`src_ip` 不是 Windows 日志中默认的索引字段(就像在 Linux `auth.log` 中那样),因此必须使用 `rex` 从原始事件文本中提取它——具体来说,是从事件内部的“Source Network Address”字段中提取。
### 核心要点
与项目 1 具有相同的检测*形态*(按来源统计失败次数、阈值、排序)——但日志格式完全不同(结构化的 Windows Event XML/文本对比 Linux syslog)。认识到底层模式是相同的,即使原始数据看起来毫无共同之处,这就是死记硬背查询与真正理解检测工程的区别所在。
## 项目 4 — 权限提升检测
**攻击面:** 本地 Linux 权限边界 (sudo/su)
**模拟技术:** 后渗透行为——已经有初步立足点的攻击者试图成为 root
**日志源:** Linux Audit Daemon (`auditd`) → `/var/log/audit/audit.log` → `index=linux`, `sourcetype=linux_audit`
### auditd 到底是什么
与 `auth.log`(仅记录登录事件)不同,**auditd 会监视你配置为监视的特定文件和命令**,无论成功还是失败,都会记录每一次访问/执行尝试。它是用于此类精细监控的标准 Linux 工具。
### 已配置规则
```
sudo auditctl -w /etc/passwd -p wa -k passwd_changes # watch for new/modified user accounts
sudo auditctl -w /etc/sudoers -p wa -k sudoers_changes # watch for sudo permission tampering
sudo auditctl -w /usr/bin/sudo -p x -k sudo_usage # log every sudo execution
sudo auditctl -w /bin/su -p x -k su_usage # log every su execution
```
`-p wa` = 监视写入和属性更改事件。`-p x` = 监视执行事件。`-k` 使用可搜索的关键字标记每个规则。
### 模拟内容
```
sudo cat /etc/shadow # attacker reading sensitive credential file
sudo useradd hacker # attacker creating a backdoor account
sudo -l # attacker enumerating their own privileges
su root # attacker attempting to become root directly
```
### 检测查询
```
index=linux sourcetype=linux_audit
| rex field=_raw "key=\"(?P[^\"]+)\""
| rex field=_raw "acct=\"(?P[^\"]+)\""
| search rule_key="sudo_usage" OR rule_key="passwd_changes" OR rule_key="su_usage"
| table _time, host, rule_key, account
| sort _time
```
**生效原因:** 每个 audit 事件都带有一个与触发它的 `auditctl` 规则相匹配的 `key=` 字段,此外还有 PAM 身份验证结果 (`res=success` / `res=failed`),显示提升操作是否真正成功。对 `key` 值进行过滤,将原始的 audit 噪音直接与你决定值得监视的特定行为联系起来。
### 核心要点
项目 1-3 检测的是**身份验证失败**,而这个项目检测的是**访问后行为**——一个完全不同(且可以说更重要)的类别。暴力破解尝试动静很大且易于发现;而已经潜入的攻击者悄悄提升权限,正是真实 SOC 团队最担心的场景。
## 项目 5 — SOC 仪表板
**目标:** 将项目 1-4 组合成一个单一的、始终是最新的可视化概览——就像真正的 SOC 分析师监控实时环境那样,而不是整天运行临时搜索。
### 使用的面板类型
| 类型 | 用途 | 本实验中的示例 |
|---|---|---|
| **Single Value** | 一眼即可看到的一个关键数字 | 今日总事件数 |
| **Statistics Table** | 包含细节的行/列 | 按IP统计的SSH暴力破解尝试 |
| **Bar Chart** | 比较不同类别 | 攻击者 IP 前 10 名 |
| **Line Chart** | 随时间变化的趋势 | 每小时的攻击量 |
### 跨索引面板示例(将每个项目联系在一起)
```
index=auth OR index=web OR index=main OR index=linux
| rex field=_raw "(?P\d+\.\d+\.\d+\.\d+)"
| stats count as total_attacks by src_ip
| where total_attacks >= 5
| sort - total_attacks
| head 10
```
### 为什么自动刷新很重要
只有在手动重新加载时才更新的仪表板并不比保存的搜索好多少。设置 5 分钟的自动刷新间隔意味着屏幕的表现就像一个真正的监控控制台——任何人都不需要刻意去检查,异常情况就会显示出来。
### 核心要点
仪表板只是在视觉上排列的几个保存的搜索。其价值不在于工具,而在于**什么内容应该出现在分析师看到的第一块屏幕上**这一设计决策——在五秒钟内回答“谁在攻击我们”、“什么类型的攻击”以及“什么时候”这些问题。
## 项目 6 — Windows 事件日志深入分析
**目标:** 超越基础失败登录检测(项目 3),进一步学习 Windows 安全事件更广泛的词汇——这是 SOC 分析师面试中最常被考察的主题。
### 模拟技术
```
net user hacker Password123! /add # Event 4720 — new account created
net localgroup administrators hacker /add # Event 4732 — added to privileged group
whoami; ipconfig; net user # Event 4688 — process creation (recon commands)
wevtutil cl Security # Event 1102 — security log cleared (anti-forensics)
```
### Event ID 参考(值得记忆的部分)
| Event ID | 含义 | 重要性 |
|---|---|---|
| 4624 | 成功登录 | 基线——谁合法地在系统上 |
| 4625 | 登录失败 | 暴力破解指标 |
| 4648 | 使用显式凭据登录 | 经典的横向移动 / pass-the-hash 指标 |
| 4672 | 登录时分配了特殊权限 | 标记管理员级别的会话 |
| 4688 | 创建了新进程 | 检测恶意或可疑二进制文件的执行 |
| 4720 | 创建了用户账户 | 创建后门账户 |
| 4732 | 将用户添加到安全组 | 通过组成员身份进行权限提升 |
| 4740 | 账户被锁定 | 通常是暴力破解的副作用 |
| 1102 | 审核日志被清除 | 极高危机信号——合法的管理员几乎从不这样做;攻击者这样做是为了抹除证据 |
### 检测查询 —— 将事件串联成一个故事
```
index=main sourcetype="WinEventLog:Security"
| search EventCode=4720 OR EventCode=4732 OR EventCode=4688 OR EventCode=1102
| eval event_type=case(
EventCode=4720, "New User Created",
EventCode=4732, "Added to Admin Group",
EventCode=4688, "Process Created",
EventCode=1102, "Log Cleared"
)
| table _time, host, event_type, Account_Name
| sort _time
```
**生效原因:** `eval ... case()` 将原始数字事件代码转化为人类可读的叙述列。孤立地查看 4720 只会告诉你*创建了一个用户*。看到 4720 紧接着是 4732,然后是 1102,就讲述了一个**完整的攻击故事**:创建账户 → 提升为管理员 → 掩盖痕迹。将一系列单独看来信号较低的事件关联成一个时间线,是 SOC 分析师的一项核心技能。
### 核心要点
没有任何单一的 Event ID 可以单独作为遭受入侵的确凿证据——4720(新用户)在合法情况下也经常发生。重要的是**顺序和上下文**:在周一早上进行 HR 入职时属于常规操作的事件,在凌晨 2 点发生且随后伴随安全日志被清除,这就是一场灾难。
## 项目 7 — Suricata 网络 IDS 集成
**目标:** 在项目 1-6 构建的所有内容(完全是**基于日志**的,意思是:如果应用程序不写日志条目,就没有检测)之上,添加网络级别的检测。Suricata 直接检查原始网络流量,无论单个应用程序选择记录什么,它都能捕获活动。
### 架构新增
```
Kali (attacker) → raw network traffic → Ubuntu (Suricata sniffing enp0s3) → eve.json → Splunk (index=suricata)
```
### 关键安装/配置教训:捕获接口不匹配
Suricata 的 `af-packet` 捕获模式必须指向正确的真实网络接口名称(在本实验中是 `enp0s3`),而不是像 `eth0` 这样的通用占位符。这里的不匹配会导致静默的崩溃循环(`systemd` 不断重启服务,报错:`failed to find interface: No such device`)——解决方法是通过 `ip a` 确认实际接口,并更新 `suricata.yaml` 中的每一行 `interface:` 使其匹配。
### 关键的数据管道教训:“未配置的索引”陷阱
即使 Suricata 正确运行并且 forwarder 成功发送了数据,**Splunk 也会静默丢弃指向接收端尚不存在的索引的事件**:
```
Received event for unconfigured/disabled/deleted index=suricata ... Dropping them as lastChanceIndex setting in indexes.conf is not configured.
```
这对于任何 SIEM 都是一个很好的通用教训:**在 forwarder 开始发送之前,接收/索引端必须已经存在该索引,否则这些数据将永远丢失**——一旦 Splunk 丢弃了它,就没有追溯恢复的方法。
### 检测查询 —— 为 JSON 引入 `spath`
与之前所有的项目(需要手动进行 `rex` 提取的纯文本日志)不同,Suricata 的 `eve.json` 是**结构化 JSON**,因此 Splunk 可以自动解析它:
```
index=suricata sourcetype=suricata
| spath
| where event_type="alert"
| table _time, src_ip, dest_ip, alert.signature, alert.severity
| sort - _time
```
按网络级别的告警量统计排名靠前的攻击者:
```
index=suricata sourcetype=suricata event_type=alert
| spath
| stats count by src_ip
| sort - count
```
### 观察结果
从单次 Kali 攻击会话中捕获了 43 条告警,包括真实的 Emerging Threats 规则集特征,如 `ET WEB_SERVER ColdFusion componentutils access` 和 `SURICATA HTTP Host header ambiguous`——这是由社区维护的真实检测特征,而不是自定义规则,在针对真实扫描流量时正确触发。
### 核心要点
基于日志的检测(项目 1-6)和基于网络的检测(项目 7)是**互补的层**,而不是彼此的替代品。从不接触受监视日志文件的攻击者(例如,未获得应用程序响应的原始端口扫描)对于 auth.log 或 Apache 日志是不可见的——但 Suricata 可以看到流量本身。正是由于这个原因,真实的 SOC 环境会同时运行这两个层。
## 完整项目摘要表
| # | 项目 | 攻击面 | 使用工具 | 日志源 | 检测方法 |
|---|---|---|---|---|---|
| 1 | SSH 暴力破解 | Linux SSH (22) | Hydra | auth.log | 按源 IP 统计失败认证次数 |
| 2 | Web 攻击检测 | Apache HTTP (80) | Nikto, Gobuster | apache access.log | 请求速率/体量异常 |
| 3 | RDP 暴力破解 | Windows RDP (3389) | Hydra | Windows 安全日志 | 按源 IP 统计 Event 4625 |
| 4 | 权限提升 | 本地 Linux 权限边界 | sudo, su, useradd | auditd audit.log | 受监视文件/命令执行 |
| 5 | SOC 仪表板 | —(聚合层) | — | 上述所有 | 跨来源的视觉关联 |
| 6 | Windows 事件日志深入分析 | Windows 本地系统 | net.exe, wevtutil | Windows 安全日志 | 多事件序列/故事构建 |
| 7 | Suricata IDS | 网络层(所有端口) | nmap, Nikto | eve.json | 基于特征的网络 IDS |
## 本实验室展示的内容(用于简历/面试背景提升)
- 从头开始构建并进行网络配置的多虚拟机实验环境(VirtualBox 网络、静态/DHCP IP 管理、虚拟机间连接排错)
- 跨多个异构日志源(syslog、Windows Event Log、auditd、JSON/IDS)配置 Splunk Universal Forwarder 管道
- 使用正则表达式提取 (`rex`)、结构化 JSON 解析 (`spath`)、聚合 (`stats`) 和关联逻辑 (`eval case()`) 编写 SPL 检测
- 认识到基于特征的检测何时会失效,并转向行为/体量检测
- 独立诊断真实的基础设施问题:服务崩溃循环、网络接口配置错误、由于缺少索引配置导致 SIEM 数据被静默丢弃、身份验证/凭据重置
- 理解基于主机的日志记录与基于网络的 IDS 检测之间的分层关系
## 建议的后续步骤
- **威胁狩猎实践** —— 不要等待预先构建的检测触发,而是以 MITRE ATT&CK 框架为指导,练习主动查询原始数据以寻找异常
- **将仪表板面板转化为计划告警**,并使用实际的通知操作(电子邮件、webhook),而不是仅仅进行视觉检查
- **添加第四类日志源** —— DNS 查询日志或防火墙日志 —— 以进一步扩大检测覆盖范围
标签:Metaprompt, PE 加载器, Web报告查看器, 安全实验室, 安全运营中心(SOC), 密码管理, 网络安全, 隐私保护