Noxiidus/tracehound

GitHub: Noxiidus/tracehound

一款零依赖的 Linux 主机取证 triage 工具,将多种系统日志和取证构件合并为统一 UTC 时间线并自动检测攻击者行为。

Stars: 1 | Forks: 0

# tracehound **Linux DFIR 取证 —— 将主机取证构件解析为统一的时间线,并揭示攻击者行为。** [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/Noxiidus/tracehound/actions/workflows/ci.yml) [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![更新日志](https://img.shields.io/badge/changelog-CHANGELOG.md-orange)](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, 子域名变形, 库, 应急响应, 数字取证, 无后门, 日志解析, 红队行动, 自动化脚本, 证书伪造, 逆向工具