tahovig/poc-logids
GitHub: tahovig/poc-logids
一个基于 Go 的迷你日志 IDS 命令行工具,通过解析 auth.log 检测 SSH 暴力破解尝试并支持实时监控告警。
Stars: 0 | Forks: 0
# poc-logids — 基于日志的异常检测工具
[](https://github.com/tahovig/poc-logids/actions/workflows/ci.yml)
一个用于解析日志数据并标记可疑模式的 Go CLI —— 一款用于日志分析的“迷你 IDS”。首个检测目标:auth.log 风格日志中的 SSH 暴力破解尝试(在特定时间窗口内,来自单一来源的重复身份验证失败尝试)。
这是一系列从软件工程转向网络安全工程的作品集中的第二个项目。第一个项目是:[poc-osint](https://github.com/tahovig/poc-osint),一款自动化的子域名侦察工具。
## 状态
```
$ poc-logids -file resources/loghub-linux/Linux.log
SOURCE ATTEMPTS FIRST SEEN LAST SEEN USERS TRIED
unknown.sagonet.net 23 Jun 11 09:45:45 Jun 11 09:46:48 root
218.188.2.4 11 Jun 12 01:12:13 Jun 12 01:12:27 test
218.38.14.205 13 Jun 12 14:10:40 Jun 12 14:10:54
...
(327 alerts total, spanning the full 263.9-day dataset)
$ poc-logids -file auth.log -follow
No brute-force activity detected.
Watching auth.log for new activity (threshold=5, window=1m0s)... press Ctrl+C to stop
[ALERT] Jul 17 22:41:05 source=203.0.113.7 attempts=5 users=root
```
针对 [loghub](https://github.com/logpai/loghub) 完整的真实 Linux syslog 数据集(`resources/loghub-linux/`)运行 —— 跨越 263.9 天的真实生产数据,而非合成数据。该数据集确实跨越了日历年的边界,这就是为什么解析器会在 `Dec 31 -> Jan 1` 之间推断出一致的年份(见 `internal/parser`),而不是假设所有记录都发生在同一年。
## 用法
```
go build -o poc-logids ./cmd/poc-logids
./poc-logids -file [-json] [-threshold N] [-window 60s] [-follow]
```
- `-threshold`(默认为 5)—— 来自单一来源的最低失败尝试次数,达到该值即标记为暴力破解。
- `-window`(默认为 60s)—— 连续尝试之间允许的最大间隔,在此范围内才会被视为同一个爆发期。
- `-json` —— 输出 JSON 格式,而不是表格/单行格式。
- `-follow` —— 在初始扫描之后,持续监视文件并在检测到新警报时立即打印(按 Ctrl+C 停止)。与只有在爆发期完全结束后才报告的批量扫描不同,`-follow` 会在爆发期首次达到 `-threshold` 的瞬间立即发出警报,因为如果等到正在进行的攻击停止后才去报告,就违背了实时监控的意义。
## 工作原理
有几个决策值得一提,因为它们源于真实的 bug/数据,而不是一开始就显而易见的:
- **syslog 时间戳的年份推断。** 经典的 syslog 格式(`Jun 14 15:16:01 ...`)没有年份字段。解析器会按顺序处理日志行,并在某行的月份早于前一行的月份时,增加内部年份计数器 —— 这是一种标准的启发式方法,因为只有真正的 Dec→Jan 年份更迭才会触发它,而不是普通的乱序抖动。在确认完整的 loghub 数据集确实跨越了年份边界后,发现并修复了这个问题;验证了真实年份更迭期间的时间差计算结果是秒数,而不是错误的约 364 天跳跃。
- **`-follow` 中的日志轮转处理。** 处理了 logrotate 的两种常见策略:重命名+重建(不断重试打开该路径,直到新文件出现)和原位 `copytruncate` 截断。截断的情况曾存在一个真实的约 50% 概率触发的竞态条件 —— 截断后紧接着重写,可能会导致在下一次检查时文件大小并不小于上次读取的位置,因此单纯的大小比较会漏掉它。通过有界的尾部内容验证修复了该问题,而不是仅仅依赖大小。
- **实时警报在爆发期达到阈值的瞬间触发**,而不是在结束后。专门为此设计了一个独立的流式检测器(`internal/detector.Live`)—— 批量检测器的“等到爆发期完全结束”方法适用于分析静态文件,但对于实时监控毫无用处,因为在实时监控中,你希望在攻击正在发生时就能得知。
- **在关键之处使用真实数据而非合成数据。** 自动化测试使用合成夹具(确定性的,无实时依赖),但演示/验证数据使用的是 [loghub](https://github.com/logpai/loghub) 真实的生产 syslog 数据 —— 真实的攻击者行为,而非虚构的日志行。
## 仓库结构
- `cmd/poc-logids/` —— CLI 入口点
- `internal/parser/` —— 从日志行中提取失败的 SSH 身份验证事件(syslog `pam_unix` 和现代 OpenSSH 格式),并在 Dec 31 -> Jan 1 边界之间推断一致的年份,因为 syslog 时间戳不携带年份
- `internal/detector/` —— 标记每个来源的暴力破解爆发期(批量模式,以及用于 `-follow` 的流式变体)
- `internal/output/` —— JSON / ASCII 表格 / 单行渲染
- `internal/tail/` —— 用于 `-follow` 的基于 `fsnotify` 的文件追踪,处理日志轮转
- `resources/` —— 辅助/参考材料(非代码),包括真实世界的日志样本
## 技术栈
Go。完整的架构设计和理由请参见 `CLAUDE.md`。
标签:EVTX分析, Go, Ruby工具, 动态分析, 异常检测, 文档结构分析, 日志审计