elin-olsson/chain-watch

GitHub: elin-olsson/chain-watch

零依赖的 Linux 安全日志关联 CLI 工具,通过多源日志时间窗口关联检测 SSH 暴力破解、端口扫描、横向移动等多阶段攻击链。

Stars: 0 | Forks: 0

chain-watch

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/fba208f7ea112720.svg)](https://github.com/elin-olsson/chain-watch/actions/workflows/ci.yml) 一个用于 Linux 系统的 Python CLI 安全日志关联工具。 同时读取多个日志源 —— `auth.log`、UFW/firewalld 日志、`auditd` 和 systemd journal —— 并根据源 IP 和时间窗口关联事件,以检测多阶段攻击链:端口扫描后接暴力破解、暴力破解后接成功登录,以及登录后的横向移动。 ## 前置条件 - Python 3.10 或更高版本 - 运行时无需额外的外部包 —— 仅依赖标准库 检查你的 Python 版本: ``` python3 --version ``` ## 安装说明 克隆仓库并导航到工具目录: ``` git clone https://github.com/elin-olsson/chain-watch.git cd chain-watch ``` 无需安装任何依赖。直接运行: ``` sudo python3 chainwatch.py ``` 安装 `pytest` 以运行测试套件: ``` pip install pytest ``` ## 使用方法 ``` sudo python3 chainwatch.py [LOG_DIR] [options] ``` ``` # 扫描默认 log 路径(自动检测) sudo python3 chainwatch.py # 扫描来自特定目录的 log sudo python3 chainwatch.py /var/log # 使用自定义 correlation window(默认:600 s) sudo python3 chainwatch.py --window 300 # 按时间范围 Filter sudo python3 chainwatch.py --since 03:00 --until 05:00 sudo python3 chainwatch.py --since "2026-04-21 00:00" # 实时 Watch log 并对新 incidents 发出 alert(Ctrl+C 停止) sudo python3 chainwatch.py --follow sudo python3 chainwatch.py --follow --interval 10 # 从 systemd journal 读取(在 Fedora/RHEL/Arch 上有用) sudo python3 chainwatch.py --journal sudo python3 chainwatch.py --journal --since 06:00 sudo python3 chainwatch.py --journal --follow # 将 results 写入 HTML report sudo python3 chainwatch.py --html report.html # 将 results 写入 JSON sudo python3 chainwatch.py --json report.json # 显式 Specify log 文件 sudo python3 chainwatch.py \ --auth-log /var/log/auth.log \ --ufw-log /var/log/ufw.log \ --audit-log /var/log/audit/audit.log # Fedora / Red Hat 路径 sudo python3 chainwatch.py \ --auth-log /var/log/secure \ --ufw-log /var/log/messages \ --audit-log /var/log/audit/audit.log ``` ### 参数标志 | 标志 | 描述 | |---|---| | `--window SECONDS` | 关联时间窗口(以秒为单位,默认值:600) | | `--since TIME` | 忽略 TIME 之前的事件(格式:`HH:MM`、`HH:MM:SS` 或 `YYYY-MM-DD HH:MM[:SS]`) | | `--until TIME` | 忽略 TIME 之后的事件(格式与 `--since` 相同) | | `--journal` | 通过 `journalctl` 从 systemd journal 读取(与文件源合并) | | `--follow` | 监视日志文件并实时对新事件发出警报 | | `--interval SECONDS` | `--follow` 模式的轮询间隔(以秒为单位,默认值:5) | | `--json FILE` | 将 JSON 报告写入 FILE | | `--html FILE` | 将独立的 HTML 报告写入 FILE | | `--auth-log FILE` | 显式指定 auth.log / secure 的路径 | | `--ufw-log FILE` | 显式指定防火墙日志的路径 | | `--audit-log FILE` | 显式指定 audit/audit.log 的路径 | ## 日志来源 | 来源 | 默认路径 | 提取的事件 | |---|---|---| | auth.log / secure | `/var/log/auth.log`、`/var/log/secure` | SSH 暴力破解、成功登录、sudo 使用记录 | | 防火墙日志 | `/var/log/ufw.log`、`/var/log/kern.log`、`/var/log/messages` | 包含源 IP 和端口的被阻止/允许的数据包 | | auditd | `/var/log/audit/audit.log` | execve 系统调用、用户添加/删除、PAM 认证事件 | | systemd journal | `journalctl`(通过 `--journal`) | SSH 登录、sudo 使用记录、内核防火墙事件 | 当没有提供显式路径时,chain-watch 会自动搜索默认路径。所有解析器都能优雅地处理缺失或不可读的文件 —— 如果某个日志源不存在,该来源将被静默跳过。 `--journal` 标志使用 `journalctl` 从 systemd journal 读取数据,并将结果与任何基于文件的来源合并。这在基于 systemd 的发行版(Fedora、Arch、最近的 Ubuntu)上特别有用,因为这些系统的日志主要存储在 journal 中,而不是纯文本文件中。结合 `--since` 使用可以将查询限制在特定的时间范围内,避免读取完整的 journal 历史。 ## 检测到的攻击链 chain-watch 可检测六种攻击模式。事件在滚动时间窗口内按源 IP 分组,并在模式匹配时进行升级告警。 **brute_force** `[medium]` 在同一时间窗口内,来自同一源 IP 的五次或更多次失败登录。这是自动化密码喷射或 SSH 字典攻击的典型特征。通过 `auth.log` / `secure` 检测。 **brute_then_login** `[critical]` 在同一窗口内,来自同一 IP 的暴力破解集群随后出现了成功登录。表明密码已被猜出或找到。所有导致此结果的失败尝试和成功登录都将作为关联事件被包含在内。 **portscan_then_login** `[high]` 来自某 IP 跨越至少两个不同目标端口的防火墙阻止事件,随后在同一窗口内出现了来自同一 IP 的登录尝试。两个端口的最小值过滤掉了属于正常背景噪音的单一阻止连接。每个阻止事件和认证事件都会被单独配对 —— 只有彼此在时间窗口内的事件才会被包含,因此数小时前的陈旧阻止事件不会被错误地归因于随后的登录尝试。 **lateral_movement** `[high / critical]` 成功登录后,同一用户在时间窗口内执行了可疑的 execve(通过 auditd)或 `sudo` 调用(通过 auth.log)。按用户身份而非 IP 进行关联 —— 事件会链接回先前 SSH 会话的 IP。如果使用了网络工具(`wget`、`curl`、`nc`、`ncat`、`netcat`)或通过 `sudo` 提权到 root,严重性为 `critical`;如果是 shell 生成(`bash`、`sh`)或通过 `sudo` 切换到任何其他用户,则为 `high`。 **credential_stuffing** `[high]` 在同一时间窗口内,来自同一源 IP 尝试了五个或更多不同的用户名。与侧重于尝试次数的 brute_force 不同,凭证填充的目标是广度 —— 尝试使用泄露的凭证数据库攻击许多不同的账户,每个账户仅尝试一次或少数几次。这两种攻击链可以针对同一 IP 独立触发。 **account_manipulation** `[critical]` 成功登录后,同一用户在时间窗口内创建了用户账户(`ADD_USER`)或删除了用户账户(`DEL_USER`),这通过 auditd 检测。这是入侵后活动的强烈指标 —— 创建后门账户或删除合法用户以锁定管理员。 | 攻击链 | 严重性 | 触发条件 | |---|---|---| | `brute_force` | medium | 窗口内来自同一 IP 的 ≥ 5 次失败登录 | | `brute_then_login` | critical | 窗口内来自同一 IP 的 brute_force 集群 + 成功登录 | | `portscan_then_login` | high | 窗口内来自同一 IP 的防火墙阻止 + 登录尝试 | | `lateral_movement` | high / critical | 窗口内成功登录 + 同一用户执行的 suspicious execve | | `credential_stuffing` | high | 窗口内来自同一 IP 尝试的 ≥ 5 个不同用户名 | | `account_manipulation` | critical | 窗口内成功登录 + 同一用户的 ADD_USER 或 DEL_USER | ## 支持的防火墙 chain-watch 可以在同一个防火墙日志中检测来自以下任何防火墙的数据包日志行: | 防火墙 | 检测格式 | |---|---| | UFW | `[UFW BLOCK]` / `[UFW ALLOW]` | | firewalld | `FINAL_REJECT:` / `IN__DROP:` / `IN__REJECT:` / `IN__ACCEPT:` | | iptables | `DROPPED:` / `REJECTED:` | | nftables | `nft ...:` 前缀 | 同一日志文件中来自多种防火墙类型的行会被正确处理 —— 这在 `kern.log` 或 `/var/log/messages` 积累了来自多个来源的输出时很常见。 ## 输出示例 在具有多个安全事件的实时系统上运行: ``` ══════════════════════════════════════════════════════════════ chain-watch — Attack Chain Correlation Report ══════════════════════════════════════════════════════════════ Generated 2026-04-21 03:47:12 Window 600 s Parsed 847 events (512 auth · 298 ufw · 37 audit) Incidents 3 Top attacking IPs 185.220.101.47 23 attempts ████████████████████ 45.95.147.208 7 attempts ██████ 80.82.70.202 7 attempts ██████ Most targeted ports 22/TCP 18 blocks ████████████████████ 3306/TCP 4 blocks ████ 8080/TCP 2 blocks ██ Events per hour 03:00 ████████████████████ 47 04:00 █████ 12 ────────────────────────────────────────────────────────────── [CRITICAL] #1 brute_then_login — 185.220.101.47 2026-04-21 03:12:04 → 03:22:47 (10m 43s) · 11 events ────────────────────────────────────────────────────────────── 03:12:04 failed_login user=root ip=185.220.101.47 03:13:51 failed_login user=root ip=185.220.101.47 03:15:22 failed_login user=root ip=185.220.101.47 03:17:09 failed_login user=root ip=185.220.101.47 03:19:33 failed_login user=root ip=185.220.101.47 03:20:05 failed_login user=admin ip=185.220.101.47 03:20:14 failed_login user=admin ip=185.220.101.47 03:20:29 failed_login user=admin ip=185.220.101.47 03:21:01 failed_login user=admin ip=185.220.101.47 03:21:44 failed_login user=admin ip=185.220.101.47 03:22:47 successful_login user=admin ip=185.220.101.47 ────────────────────────────────────────────────────────────── [HIGH ] #2 portscan_then_login — 45.95.147.208 2026-04-21 03:31:02 → 03:38:15 (7m 13s) · 4 events ────────────────────────────────────────────────────────────── 03:31:02 fw_block src=45.95.147.208 port=22/TCP 03:33:17 fw_block src=45.95.147.208 port=80/TCP 03:35:44 fw_block src=45.95.147.208 port=443/TCP 03:38:15 failed_login user=root ip=45.95.147.208 ────────────────────────────────────────────────────────────── [MEDIUM ] #3 brute_force — 80.82.70.202 2026-04-21 04:01:09 → 04:06:52 (5m 43s) · 7 events ────────────────────────────────────────────────────────────── 04:01:09 failed_login user=pi ip=80.82.70.202 04:02:18 failed_login user=pi ip=80.82.70.202 04:03:32 failed_login user=ubuntu ip=80.82.70.202 04:04:01 failed_login user=ubuntu ip=80.82.70.202 04:04:55 failed_login user=admin ip=80.82.70.202 04:05:47 failed_login user=admin ip=80.82.70.202 04:06:52 failed_login user=root ip=80.82.70.202 ══════════════════════════════════════════════════════════════ ``` ### HTML 报告 使用 `--html ` 生成带有颜色编码的严重性条带和可折叠事件列表的独立 HTML 报告: ``` sudo python3 chainwatch.py --html report.html ``` 在任何浏览器中打开 `report.html` —— 无需网络连接。 ### JSON 导出 使用 `--json ` 获取适用于脚本编写或与 SIEM 集成的机器可读输出: ``` sudo python3 chainwatch.py --json report.json ``` 输出包含时间戳、解析的事件计数以及一个事件对象数组,数组中的对象包含 `chain_type`、`source_ip`、`severity`、`start_time`、`end_time`、`duration_seconds` 和 `events` 字段。 ## 工作原理 来自每个日志源的事件会被解析并标准化为带有统一 `timestamp` 和 `source_ip` 的类型化事件字典。随后,这些事件将根据源 IP 在所有四个来源中进行分组。对于每个 IP,chain-watch 会应用一个滑动时间窗口:当来自多个来源的事件在窗口内聚集时,相关的攻击链条件将按严重性顺序进行评估 —— 对于同一个集群,`brute_then_login` 将取代普通的 `brute_force`。 `lateral_movement` 和 `account_manipulation` 是例外:auditd 记录带有用户身份但不包含 IP。chain-watch 将它们链接回该用户在窗口内最近一次成功登录的 IP,从而无需内核级别的网络跟踪即可实现跨源关联。 ## 依赖项 无运行时依赖 —— 仅依赖标准库。安装 `pytest` 以运行测试套件: ``` pip install pytest python -m pytest tests/ ``` | 包 | 版本 | 用途 | |---|---|---| | `pytest` | ≥ 9.0.3 | 仅用于测试套件 —— 运行时不需要 |

本 Logo 归 shadowfox.se © 2026 版权所有 —— 保留所有权利,不受 MIT 许可证涵盖。

标签:AMSI绕过, auditd, auth.log, CLI 工具, firewalld, Linux 安全, PE 加载器, Python, Python3.6, SSH 暴力破解检测, systemd journal, UFW, 多阶段攻击检测, 威胁检测, 子域名变形, 子域名枚举, 安全事件响应, 安全日志关联, 插件系统, 无后门, 横向移动检测, 端口扫描检测, 系统安全, 逆向工具, 防火墙日志, 零依赖