zachvessey16/ssh-bruteforce-detection
GitHub: zachvessey16/ssh-bruteforce-detection
基于自模拟攻击日志构建的 SSH 暴力破解 Sigma 检测规则项目,涵盖从攻击复现到多后端查询转换的完整检测工程流程。
Stars: 0 | Forks: 0
# 从零开始检测 SSH 暴力破解 (T1110.001)
我想做一个检测工程项目,而不是简单地将别人的安全公告转换为规则(我已经为涵盖 CISA/NSA/FBI 公告的 [SigmaHQ PR](https://github.com/SigmaHQ/sigma/pull/6145) 做过这件事)。这次正好相反:选择一种技术,自己模拟攻击,查看其产生的真实日志,并据此编写检测规则。
这项技术是 [T1110.001,密码猜测](https://attack.mitre.org/techniques/T1110/001/),特别是针对 SSH 的攻击。这是蓝队检测中最经典的场景之一,但这正是关键所在。如果你不能从基本原理出发编写出可靠的暴力破解检测规则,那么更花哨的东西也无济于事。
## 环境配置
以下所有操作都在一台本地 Ubuntu 机器上运行:
- 已安装并运行 `openssh-server`
- 一个用完即弃的测试账号 (`labtarget`),设置了已知的弱密码
- 一个包含常见错误猜测的字典,真正的密码被安插在末尾附近,这样日志就会显示出真实的模式:一连串失败,紧接着一次成功
`simulate_attack.sh` 使用 `sshpass` 循环遍历密码字典,尝试为每个条目进行登录。没什么特别的,这大致就是低成本的自动化暴力破解脚本的样子。
## 日志的实际内容
从 `journalctl -u ssh` 中提取,每次失败的尝试记录如下:
```
Jul 28 17:03:21 MSI sshd-session[20384]: pam_unix(sshd:auth): authentication failure; ... user=labtarget
Jul 28 17:03:23 MSI sshd-session[20384]: Failed password for labtarget from 127.0.0.1 port 57058 ssh2
Jul 28 17:03:25 MSI sshd-session[20384]: Connection closed by authenticating user labtarget 127.0.0.1 port 57058 [preauth]
```
使用不同的源端口重复了九次,然后是:
```
Jul 28 17:04:00 MSI sshd-session[20476]: Accepted password for labtarget from 127.0.0.1 port 38112 ssh2
```
大约 40 秒内来自同一源的九次失败,然后是一次成功。完整样本位于 `logs/auth_sample.log` 中。
## 规则
单凭一行 `Failed password` 不值得发出警报,人们经常会输错密码。重要的是单个来源的*频率*。因此这是两条规则:
**`ssh_failed_password_base.yml`** 是基础检测,它只匹配失败的 sshd 密码认证事件。没什么巧妙的,它存在的意义是为了让下面的关联规则有东西可以计数。
**`ssh_bruteforce_correlation.yml`** 是实际逻辑所在。它使用 Sigma 的 `event_count` 关联类型,按 `src_ip` 对基础规则的匹配进行分组,并在 2 分钟窗口内达到 5 次或以上时触发。我最初尝试使用 `value_count`(它计算某个字段的*去重后的不同值数量*,适用于针对大量用户名的密码喷洒),但对于“同一个地方重复多次相同失败”的情况,它并不适用。对于简单的阈值控制,`event_count` 才是你需要的。
我选择 2 分钟内 5 次作为起点,这带有一定的随意性:这个值足够低以捕获脚本化尝试,又足够高,以至于一个因为沮丧而连续三次输错密码的人不会触发警报。在真实环境中,我会根据实际的认证失败基线来调整此设置,而不是凭空猜测。
## 转换与测试
使用 `sigma-cli` (pySigma) 验证规则并将其转换为真实的后端查询:
```
sigma check rule/ssh_failed_password_base.yml
# Found 0 个 errors、0 个 condition errors 和 0 个 issues。
```
Splunk SPL:
```
Message="*Failed password for*"
| bin _time span=2m
| stats count as event_count by _time src_ip
| search event_count >= 5
```
Elastic ES|QL:
```
from * metadata _id, _index, _version | where Message like "*Failed password for*"
| eval timebucket=date_trunc(2minutes, @timestamp) | stats event_count=count() by timebucket, src_ip
| where event_count >= 5
```
两者都使用 `--without-pipeline` 顺利转换,这意味着查询是可移植的,但假设目标环境已经对原始的 sshd `Message` 字段和 `src_ip` 字段进行了标准化,在实际部署中,这将来自你日志源的字段提取机制(Splunk 的 CIM 认证模型、Elastic 的 ingest pipeline 等等)。我首先尝试通过 Splunk 内置的 `splunk_cim` pipeline 运行它,但它直接拒绝了该规则(`Rule type not yet supported by the Splunk data model CIM pipeline`),所以目前这是原始字段版本。如果我扩展这个项目,值得重新审视这个问题。
## 误报
- 许多真实用户失败的登录汇聚到一个源 IP 上的共享跳板机或 NAT 网关。此规则将需要更高的阈值,或者为已知的共享出口设置白名单。
- 配置错误的服务或 cron job 不断循环重试过期的凭据。在频率上看起来与暴力破解完全相同,但意图不同。值得检查失败登录中的 `user=`,service account 通常针对固定的、范围狭窄的用户名集合,而不是尝试登录随机账号。
- 密码管理器或带有陈旧保存凭据的脚本在转而使用备用方案前进行少数几次快速重试。通常保持在 5 次尝试阈值以下,这也是我选择该数字的部分原因。
## 下一步计划
- 针对较慢的、低频的暴力破解(每隔几分钟尝试一次)测试该规则,看看它能多容易地规避 2 分钟窗口,以及具有更低阈值的更长窗口是否能在不增加噪音的情况下捕获它。
- 添加第二个关联规则,专门标记当失败突发后紧接着来自同一源的成功登录的情况,这是比单纯失败突发更具高置信度的“攻击可能已成功”信号。
- 针对真实的认证日志而不是我自己生成的样本测试该规则,我的样本中只有一种非常干净的攻击模式。
仓库布局:
```
wordlist.txt guess list used against the test account
simulate_attack.sh script that ran the simulated attack
logs/auth_sample.log raw sshd log output from the attack
rule/ the two Sigma rules
writeup/ converted Splunk and Elastic queries
```
标签:BurpSuite集成, Reconnaissance, Sigma规则, URL发现, 安全检测, 应用安全, 攻击模拟, 目标导入, 驱动签名利用