SecurityRonin/shellhist-forensic

GitHub: SecurityRonin/shellhist-forensic

纯 Rust 实现的 shell 命令历史取证分析库,支持解析四种主流 shell 的历史记录并自动检测清除、篡改和远程执行等反取证行为。

Stars: 0 | Forks: 0

# shellhist-forensic [![shellhist-core](https://img.shields.io/crates/v/shellhist-core.svg?label=shellhist-core)](https://crates.io/crates/shellhist-core) [![shellhist-forensic](https://img.shields.io/crates/v/shellhist-forensic.svg?label=shellhist-forensic)](https://crates.io/crates/shellhist-forensic) [![Docs.rs](https://img.shields.io/docsrs/shellhist-forensic)](https://docs.rs/shellhist-forensic) [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/484666fe19034519.svg)](https://github.com/SecurityRonin/shellhist-forensic/actions) [![Sponsor](https://img.shields.io/badge/sponsor-h4x0r-ea4aaa?logo=github-sponsors)](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, 可视化界面, 命令行历史审计, 域渗透, 安全, 异常检测, 电子数据取证, 网络流量审计, 解析器, 超时处理, 通知系统