SecurityRonin/shellhist-forensic
GitHub: SecurityRonin/shellhist-forensic
纯 Rust 实现的 shell 命令历史取证分析库,支持解析四种主流 shell 的历史记录并自动检测清除、篡改和远程执行等反取证行为。
Stars: 0 | Forks: 0
# shellhist-forensic
[](https://crates.io/crates/shellhist-core)
[](https://crates.io/crates/shellhist-forensic)
[](https://docs.rs/shellhist-forensic)
[](LICENSE)
[](https://github.com/SecurityRonin/shellhist-forensic/actions)
[](https://github.com/sponsors/h4x0r)
**一个从头编写的 shell 命令历史读取器和分级异常审计器 —— 直接从磁盘解析 bash、zsh、fish 和 PowerShell PSReadLine 的历史记录,挖掘出攻击者希望你滚动滑过而忽略的清除历史记录、篡改时间戳条目以及下载并管道传递至 shell 的 payload。**
一个工作区,两个 crate:
- **[`shellhist-core`](https://crates.io/crates/shellhist-core)** —— 读取器:将 bash (`.bash_history`,`#` + 多行)、zsh (`.zsh_history`,`EXTENDED_HISTORY` `: start:elapsed;cmd` + 反斜杠续行)、fish (`fish_history`,带有 2 条规则的转义还原的近似 YAML 格式) 和 PowerShell PSReadLine (`ConsoleHost_history.txt`,反引号续行) 读取并解析为统一的 [`HistoryEntry`] 流。纯 Rust 实现,没有 `unsafe`,也没有正则表达式引擎 —— 可以读取在任何操作系统上生成的历史文件。
- **[`shellhist-forensic`](https://crates.io/crates/shellhist-forensic)** —— 审计器:将解析后的条目流转化为严重性分级的 [`forensicnomicon::report::Finding`](https://crates.io/crates/forensicnomicon),使得主机的 shell 历史记录能够与取证集群中的其他记录以统一标准进行聚合。
## 在 30 秒内审计一个历史文件
```
[dependencies]
shellhist-forensic = "0.1" # pulls in shellhist-core
```
```
use shellhist_core::parse_auto;
use shellhist_forensic::{audit, source};
// Bytes off disk + an optional filename hint; the format is auto-detected.
let entries = parse_auto(history_bytes, Some(".bash_history"));
for anomaly in audit(&entries) {
let finding = anomaly.to_finding(source("host"));
println!("[{:?}] {} — {}", finding.severity, finding.code, finding.note);
// e.g. [Some(Medium)] SHELLHIST-REMOTE-EXEC-PIPE — the command "curl … | sh" downloads and pipes …
}
```
`parse_auto` 通过字节内容嗅探格式(文件名仅用于消除平局);`audit` 对发现的内容进行分级。格式错误或被截断的历史记录绝不会引发 panic —— 它们会降级作为普通行处理。
跳过这两个步骤,直接获取 findings:
```
use shellhist_core::parse_auto;
use shellhist_forensic::audit_findings;
let entries = parse_auto(history_bytes, Some("ConsoleHost_history.txt"));
let findings = audit_findings(&entries, "host"); // Vec
```
## 异常代码
每种异常都是一个**观察结果**(“与……一致”);由检查人员得出结论。这些代码是一个稳定、已发布的契约。
| 代码 | 严重性 | 类别 | 观察内容 |
|---|---|---|---|
| `SHELLHIST-HISTORY-DISABLED` | 中 | 隐藏 | 一个幸存下来的、用于禁用或清除历史记录的命令(`unset HISTFILE`,`history -c` 等)—— 与反取证的历史篡改行为一致 (MITRE T1070.003) |
| `SHELLHIST-TIMESTAMP-REGRESSION` | 中 | 完整性 | 一个时间戳早于前一个条目的条目 —— 历史记录发生了时间倒流,与被注入或篡改日期的条目一致 |
| `SHELLHIST-REMOTE-EXEC-PIPE` | 中 | 威胁 | 一个直接通过管道传递给 shell 的下载命令(`curl … \| sh`)—— 与远程 payload 执行一致 (MITRE T1059 / T1105) |
| `SHELLHIST-PWSH-ENCODED-CMD` | 中 | 威胁 | 一个经过编码或绕过执行策略的 PowerShell 调用 —— 与混淆执行一致 (MITRE T1059.001 / T1027) |
`audit(&entries)` 返回类型化的 [`HistAnomaly`] 流;每种异常通过 `to_finding(source)` 发出一个分级的 `report::Finding`,而 `audit_findings(&entries, scope)` 则一步完成这两个操作。`source(scope)` 会为分析器的来源添加戳记。
## 读取器:跨越四种 shell 的统一流
`shellhist-core` 解码每种格式的特性,并将它们标准化为统一的 [`HistoryEntry`](`shell`,`command`,`timestamp`,`elapsed`,`paths`):
```
use shellhist_core::{parse_auto, detect, Shell};
// Detect the format without committing to a parse…
assert_eq!(detect(b": 1700000000:0;ls", None), Shell::Zsh);
// …or just parse. Multi-line commands keep their embedded newlines; zsh
// EXTENDED_HISTORY entries carry both `timestamp` and `elapsed`.
let entries = parse_auto(b": 1700000000:5;make build\n", None);
assert_eq!(entries[0].command, "make build");
assert_eq!(entries[0].elapsed, Some(5));
```
当 shell 类型已知时,可以使用针对特定格式的入口点(`shellhist_core::{bash, zsh, fish, powershell}::parse`)。
## 这与使用 `cat` 读取历史文件有何不同
历史文件看起来像纯文本,但每种 shell 编码时间戳、多行命令和流逝时间的方式各不相同 —— 而攻击者的痕迹恰好隐藏在这些缝隙中。这个工作区回答了数字取证检查人员真正需要解决的问题:
| 功能 | `cat` / 行分割器 | 本工作区 |
|---|---|---|
| 读取纯文本的每行一个命令 | ✅ | ✅ |
| bash 的 `#` 时间戳 + 多行命令 | — | ✅ |
| zsh 的 `EXTENDED_HISTORY` (`: start:elapsed;cmd`) + 反斜杠续行 | — | ✅ |
| fish 的近似 YAML 记录 + 路径关联 | — | ✅ |
| PowerShell PSReadLine 的反引号续行 | — | ✅ |
| 根据字节内容自动检测格式(文件名仅用于消除平局) | — | ✅ |
| 检测清除历史记录 / `unset HISTFILE` 的行为 | — | ✅ |
| 检测时间戳倒流(篡改日期) | — | ✅ |
| 检测下载并管道传递至 shell 的行为 | — | ✅ |
| 检测编码的 PowerShell 命令 | — | ✅ |
| 严重性分级的 `report::Finding` 输出 | — | ✅ |
| 纯 Rust 实现,`#![forbid(unsafe_code)]` | — | ✅ |
## 信任,但要验证
`shellhist-forensic` 是专为处理来自可能已被入侵系统的不可信历史文件而构建的:
- **`#![forbid(unsafe_code)]`** 贯穿两个 crate —— 没有 FFI,没有 C 绑定。它可以读取在任何操作系统上生成的历史文件。
- **面对恶意输入不会发生 panic** —— 解析是宽容的(有损 UTF-8)且经过边界检查;该工作区在生产代码中禁用了 `clippy::unwrap_used` 和 `clippy::expect_used`。被截断或乱码的文件会降级作为普通行处理,绝不会崩溃。
- **经过模糊测试** —— 五个 `cargo-fuzz` 目标(`bash`、`zsh`、`fish`、`powershell`,以及用于完整 parse→audit 流程的 `forensic`);`fuzz.yml` CI 工作流会构建并对每个目标进行冒烟测试。
- **在真实工件上得到验证** —— 分析器针对由真实 `bash` 子 shell(而非合成的测试夹具)生成的历史文件进行了端到端测试,其植入的 `curl … | sh` 和 `unset HISTFILE` 痕迹被重新挖掘出来(参见 `forensic/tests/real_data.rs`)。
```
cargo test
cargo +nightly fuzz run forensic # requires nightly + cargo-fuzz
```
## 适用场景
`shellhist-core` 是 SecurityRonin 取证系列的 shell 命令历史基础组件。它位于 PARSER 层 —— 将工件记录解释为取证含义 —— 并将分级的 findings 输入到 [`issen`](https://github.com/SecurityRonin/issen) 中以进行跨工件关联。相关的集群 crate:
| Crate | 角色 |
|---|---|
| [`forensicnomicon`](https://crates.io/crates/forensicnomicon) | **KNOWLEDGE** —— 每个分析器都会发出的共享 `report::Finding` 模型 |
| [`issen`](https://github.com/SecurityRonin/issen) | **Orchestrator** —— 连接每条取证路径并关联 findings |
| [`ntfs-forensic`](https://github.com/SecurityRonin/ntfs-forensic) | NTFS 文件系统读取器 + 异常审计器 |
[隐私政策](https://securityronin.github.io/shellhist-forensic/privacy/) · [服务条款](https://securityronin.github.io/shellhist-forensic/terms/) · © 2026 Security Ronin Ltd
标签:Rust, 可视化界面, 命令行历史审计, 域渗透, 安全, 异常检测, 电子数据取证, 网络流量审计, 解析器, 超时处理, 通知系统