Noxiidus/tracehound
GitHub: Noxiidus/tracehound
一款零依赖的 Linux 主机取证 triage 工具,将多种系统日志和取证构件合并为统一 UTC 时间线并自动检测攻击者行为。
Stars: 1 | Forks: 0
# tracehound
**Linux DFIR 取证 —— 将主机取证构件解析为统一的时间线,并揭示攻击者行为。**
[](https://github.com/Noxiidus/tracehound/actions/workflows/ci.yml)
[](https://www.python.org/)
[](LICENSE)
[](CHANGELOG.md)
将其指向 `/var/log`、已挂载的镜像或收集到的证据文件夹。它会解析所识别的内容,将所有内容合并为一个 UTC 标准化的时间线,并应用检测规则,将原始事件转化为带有 MITRE ATT&CK 映射的发现结果。
```
$ tracehound scan ./evidence --year 2024
tracehound report
======================================================================
Events parsed : 71
Time range : 2024-03-06 06:17:15 .. 2024-03-06 06:41:01 UTC
Sources : auth.log (66), wtmp (5)
Findings : 7
----------------------------------------------------------------------
[1] CRITICAL Compromised account 'root' via brute-force from 65.2.161.68
rule : THN-0002
window : 2024-03-06 06:31:31 .. 2024-03-06 06:32:44 UTC
ATT&CK :
T1110 — Brute Force (Credential Access)
T1078 — Valid Accounts (Defense Evasion)
24 failed attempts from 65.2.161.68 preceded a successful login as
'root' at 2024-03-06 06:32:44 UTC. Treat this account as compromised.
```
## 为什么需要它
分析被入侵的 Linux 主机意味着需要同时阅读 `auth.log` 和 `wtmp`,调和它们的时间戳,并在脑海中保持事件序列。这虽然可行,但无法规模化,而且很容易出现细微的错误——大多数人使用的内置 `utmp` 解析器会以**本地时间**显示时间戳,这会在不抛出任何错误的情况下,悄无声息地偏移整个调查的基准。
tracehound 每次都会以机械化的方式在 UTC 下完成这种关联。
## 安装
要求 Python 3.10 或更高版本。没有第三方运行时依赖。
```
pip install git+https://github.com/Noxiidus/tracehound.git
```
如果需要可复现性,请锁定特定版本:
```
pip install git+https://github.com/Noxiidus/tracehound.git@v0.2.0
```
对于开发,请克隆并在可编辑模式下安装测试附加组件:
```
git clone https://github.com/Noxiidus/tracehound
cd tracehound
pip install -e ".[dev]"
```
## 用法
```
# 对收集到的 artifacts 目录进行 Triage
tracehound scan ./evidence --year 2024
# 仅以 JSON 格式输出关键信息,供下游 tooling 使用
tracehound scan /var/log -f json --min-severity high -o findings.json
# 可共享的、独立的 HTML 报告
tracehound scan ./evidence -f html -o report.html
# 以 CSV 格式导出的完整 timeline,适用于电子表格或 super-timeline
tracehound scan ./evidence -f csv -o timeline.csv
# 如果发现任何内容则非零退出 —— 适用于 pipelines
tracehound scan /var/log --min-severity high --fail-on-findings
tracehound parsers # what it can read
tracehound rules # what it looks for
```
### `--year` 非常重要
Syslog 时间戳会省略年份。如果没有提示,tracehound 会假设为当前年份,这对于实时取证是正确的,但对于归档证据则是错误的。只要日志不是今年的,请传入 `--year`。文件内(12 月到 1 月)的年份跨年情况会被自动检测并处理。
## 作为库使用
```
from pathlib import Path
from tracehound import scan
result = scan([Path("evidence/")], year=2024)
for finding in result.findings:
print(f"[{finding.severity.value}] {finding.title}")
for technique in finding.attack_techniques:
print(f" {technique}")
# timeline 可独立进行查询
for event in result.timeline.by_ip("65.2.161.68"):
print(event.timestamp, event.message)
```
## 支持的取证构件
| 解析器 | 文件 | 说明 |
|---|---|---|
| `wtmp` | `wtmp`, `utmp`, `btmp` | 直接解析二进制的 384 字节记录。`btmp` 条目会被归类为失败。 |
| `lastlog` | `lastlog` | 以 UID 为索引的 292 字节数组。稀疏的零记录意味着“从未登录过”,会被跳过。 |
| `shell_history` | `.bash_history`, `.zsh_history` | 处理 `HISTTIMEFORMAT` 纪元时间戳;未注明日期的条目会被标记,而不会被静默赋予日期。 |
| `cron` | `cron`, `cron.log` | `CROND` 执行记录,并提取计划任务命令。 |
| `journal` | `journalctl -o json` 输出 | JSON Lines 或 JSON 数组。在不存在 `auth.log` 的主机上是必需的。 |
| `auth.log` | `auth.log`, `secure` | sshd、sudo、PAM、useradd/usermod/groupadd、systemd-logind。支持 syslog 和 ISO-8601 时间戳。 |
使用以下命令收集 journal 导出文件:
```
journalctl -o json --no-pager > journal.json
```
解析器的选择由显式的 `priority` 驱动,而不是导入顺序。cron 日志也是有效的 syslog,因此必须在通用的 `auth.log` 解析器占用它之前将其提供给该文件——而且这种排序不能交由格式化程序的随意意愿来决定。
## 检测规则
| ID | 严重性 | 检测内容 | ATT&CK |
|---|---|---|---|
| THN-0001 | 高 | SSH 暴力破解 —— 来自单一源的失败量 | T1110, T1110.001 |
| THN-0002 | 严重 | 暴力破解突发后的成功登录 | T1110, T1078 |
| THN-0003 | 中 | 密码喷洒 —— 大量用户,每个用户尝试次数少 | T1110.003 |
| THN-0010 | 中 | 本地账户创建 | T1136.001 |
| THN-0011 | 高 | 将账户添加到特权组 | T1098, T1548.003 |
| THN-0012 | 严重 | 后门账户 —— 创建后*随即*获得特权 | T1136.001, T1098 |
| THN-0013 | 高 | 敏感的 sudo 命令(shadow 访问、下载、日志销毁等) | T1548.003 + 每种模式 |
| THN-0020 | 中 | Shell 历史记录中记录的可疑命令 | T1059.004 + 每种模式 |
| THN-0021 | 高 | 从全局可写路径运行、将下载内容通过管道传递给 shell 等的计划任务 | T1053.003 |
| THN-0030 | 中 | 日志覆盖范围出现空隙 —— 在原本活跃的日志中出现静默 | T1070.002 |
| THN-0031 | 高 | 二进制登录数据库在记录中途被截断 | T1070.002 |
| THN-0032 | 高 | 执行过特权命令的账户缺失 Shell 历史记录 | T1070.003 |
### 调优
针对连接到真实互联网的主机运行,你每天都会得到暴力破解的发现,因为互联网每天都在对每个 SSH 端口进行暴力破解。没人阅读的报告毫无价值,因此抑制功能是一项核心特性:
```
// tuning.json
{
"known_ips": ["10.0.0.5", "203.0.113.9"], // jump hosts, monitoring
"service_accounts": ["deploy", "ansible"], // expected automation
"expected_cron": ["/opt/backup/*", "/usr/lib/sysstat/*"],
"disabled_rules": ["THN-0003"],
"brute_force_threshold": 25
}
```
```
tracehound scan /var/log -c tuning.json
```
### 自定义规则
大多数规则并不是关联性的——它们只是“标记命令与此模式匹配的此类事件”。如果这必须要求使用 Python,就会将编写规则的权力从最有可能掌握领域知识的人手中夺走,因此规则也可以这样声明:
```
# rules.yaml(也支持 JSON,无额外依赖)
rules:
- id: LOCAL-0001
title: Access to deployment secrets
severity: high
description: Someone read the deployment key material.
attack: [T1552.001]
match:
event_type: [privilege_escalation, command_executed]
command: "/etc/deploy/(id_rsa|secrets\\.env)"
- id: LOCAL-0002
title: Repeated sudo failures
severity: medium
match:
event_type: login_failure
threshold:
count: 5
window_seconds: 300
group_by: source_ip
```
```
tracehound scan /var/log -r rules.yaml
```
`match` 会缩小符合条件的事件范围——每个键都必须匹配成立,并且除了 `event_type`、`message`、`user` 和 `source_ip` 之外的任何键,都会作为 regex 与该元数据字段进行匹配。`threshold` 会将规则从“报告每次匹配”转变为“仅在足够多的匹配聚集在一起时才报告”。
YAML 需要 `pip install tracehound[yaml]`;JSON 不需要任何依赖。
## 设计说明
**解析器只负责观察,检测负责得出结论。** 解析器会将 `Failed password for invalid user admin` 转化为 `LOGIN_FAILURE` 事件并到此为止。判断其中有二十次构成了攻击是检测的工作。保持这一边界的严格性意味着,新的取证构件源可以立即受益于现有的每一条规则。
**UTC 是强制执行的,而不是假设的。** `Event.__post_init__` 会直接拒绝朴素的 datetime 对象。解析器无法意外输出本地时间,因为模型根本不会接受它。
**关联才是核心所在。** THN-0012 之所以存在,是因为仅仅创建账户是常规操作,仅仅授予特权也是常规操作——但这二者在一分钟内相继发生则绝非偶然。结合跨来源证据的发现结果,比单纯重申单行日志的发现要有价值得多。
**没有发现结果并不能证明主机是干净的。** 报告会明确指出这一点。暗示其他的取证工具比没有工具更糟糕。
## 开发
```
pip install -e ".[dev]"
pytest # tests
pytest --cov=tracehound # with coverage
ruff check src tests # lint
ruff format src tests # format
mypy # type check (strict)
```
测试套件会生成自己的取证构件——请参阅 `tests/synth.py`,它会写入字节级精确的 `wtmp` 记录,以便解析器的往返测试能够真正证明结构布局。**本仓库中未提交任何真实证据**,也绝不应该提交。
**未注明日期的证据保持未注明状态。** 纯粹的 `.bash_history` 文件根本不带时间戳。条目不是被凭空捏造时间,而是锚定到文件的 mtime,标记为 `timestamp_precision: file_mtime`,并且基于它们构建的任何发现都会在自身的文本中说明这一点。在一份报告中,悄悄暗示具备其实并不具备的精确度的时间线是一个隐患。
**缺失也是证据。** 每一条对事件做出反应的规则,随着入侵者清理得越彻底,就会变得越安静。THN-0030 到 THN-0032 颠覆了这一点:在繁忙主机上变得静默的日志、在记录中途结束的 `wtmp`、对于明显运行过命令的账户缺失的历史记录。因为缺失的证据比存在的证据更弱,所以这些规则被刻意设计得很保守——空隙是根据每个日志自身的中位间隔而不是固定数值来衡量的,并且每条发现都会在列出可疑原因的同时,说明其良性解释。
**每一个字节都有迹可循。** 每个输入文件在摄取时都会进行哈希处理,并与它的解析器和事件计数一起列在报告中,包括那些被跳过的文件及其原因。无法准确说明它读取了哪些字节的取证报告是薄弱的证据。
## 收集
在 tracehound 能够读取证据之前,必须先收集证据,而正确执行此操作也是工作的一部分。[`collect/tracehound-collect.sh`](collect/tracehound-collect.sh) 是一个无依赖的 POSIX shell 脚本,可在目标主机上运行:
```
# 在时钟已同步的主机上,记录参考时间:
REF=$(date -u +%Y-%m-%dT%H:%M:%S)
# 在 target 上:
./tracehound-collect.sh -r "$REF"
```
它执行了普通 `tar` 所不具备的三项操作:
**记录主机时钟。** 漂移只能在机器运行时进行测量。在此处捕获后,它就会变成 `--clock-offset`,从而将原本模棱两可的跨主机时间排序转变为确定的排序——请参阅下文的[时钟问题](#the-clock-problem)。
**在收集时进行哈希。** tracehound 也会在摄取时进行哈希,但这无法证明在此期间(即证据被复制、暂存和移动的过程中)发生了什么。对比这两个摘要即可填补这一空白:
```
tracehound verify ./tracehound-web01-.../manifest.json
```
不匹配的情况将被报告,并且在处理案件期间,如果没有提供 `--skip-verify` 将拒绝继续执行。离开主机后发生更改的证据是一项发现,而不是一个麻烦。
**记录自身的足迹。** 收集行为会触及主机。`footprint.txt` 列出了运行的每一条命令,因为记录污染总比假装没有污染要好。
取证构件按易失性顺序提取——运行中的进程、网络状态和已登录用户优先于磁盘上的任何内容——因为这是事后无法恢复的证据。不需要 root 权限;如果没有权限,不可读的取证构件会出现在清单的 `skipped` 列表中,而不是悄无声息地消失。
## 多主机调查
一台机器很少能说明全部情况。`tracehound case` 会扫描多台主机,并报告只有将它们的证据结合考虑时才会出现的情况:
```
tracehound case \
--host web01=/evidence/web01 \
--host db01=/evidence/db01 \
--host app02=/evidence/app02 \
--year 2024
```
| ID | 严重性 | 检测内容 |
|---|---|---|
| THN-1001 | 高 | 单一源地址在多台主机上活跃,包含首次接触的顺序 |
| THN-1002 | 严重 | 在一台主机上创建随后在另一台主机上进行身份验证的账户 |
| THN-1003 | 中 | 攻击者最先到达的主机——可能的入口点 |
| THN-1004 | 中 | 由于时钟未经验证而无法建立的时间顺序 |
### 时钟问题
漂移在一台主机内是无害的,因为每个事件都会一起偏移。**但在跨主机的情况下,同样的漂移可能会颠倒因果关系**——让事情看起来像是第二台机器入侵了第一台机器。
tracehound 绝不*推断*偏移量。当两台主机都显示存在同一攻击者时,表面上的时间差异是真正模棱两可的:这可能是漂移,也可能是攻击者只是先到达了一台主机,然后才到达另一台主机。取证构件中没有任何东西可以区分这些情况。因此,只有当你提供偏移量时,它们才会被应用:
```
tracehound case --host web01=/ev/web01 --host db01=/ev/db01 \
--clock-offset web01=0 --clock-offset db01=-137
```
如果没有提供,主机将被标记为 `assumed`,并且任何在五分钟内的时间顺序声明都会有所保留地陈述,而不是断言——THN-1003 会将自己降级为一个*候选*,而 THN-004 会解释原因。提供测得的偏移量后,同样的发现就会变成结论。
如果你在收集时使用了 `tracehound-collect.sh -r`,偏移量就已经在清单中了,无需手动提供任何内容:
```
tracehound case --manifest ev/web01/manifest.json --manifest ev/db01/manifest.json
```
这也会在分析之前,根据其收集时的摘要验证每一个取证构件。
## 路线图
- 解析器:`/etc/passwd` 和 `/etc/shadow` 差异比对、`sudoers`、`authorized_keys`、systemd units
- 用于无有意义时间戳的状态型取证构件的 `Fact` 模型
- 检测:SSH 密钥篡改、不可能旅行 登录
- 用于 Timesketch 互操作的超级时间线导出 (`l2tcsv`)
- 用于 Timesketch 互操作的超级时间线导出 (`l2tcsv`)
- 可选的 YAML 规则定义,以便无需编写 Python 即可添加检测
## 许可证
MIT —— 详见 [LICENSE](LICENSE)。
标签:Homebrew安装, PKI安全, Python, 子域名变形, 库, 应急响应, 数字取证, 无后门, 日志解析, 红队行动, 自动化脚本, 证书伪造, 逆向工具