flab75/fortilog
GitHub: flab75/fortilog
一款基于 Python 和 Streamlit 的 FortiGate 防火墙日志离线安全审计工具,通过入侵检测规则矩阵和配置审计帮助安全团队从导出的日志中发现潜在的入侵迹象。
Stars: 0 | Forks: 0
# fortilog — FortiGate 日志分析器(入侵检测)
[](https://github.com/flab75/fortilog/actions/workflows/ci.yml)
[](LICENSE)


分析 FortiCloud/FortiGate 的日志导出文件(`clé="valeur"`):自动检测类型、解析、入侵审计矩阵、多日期/多设备对比,输出 **文本报告** + **Excel 工作簿**。
## 许可证
[GNU Affero General Public License v3.0](LICENSE) — 任何分发或作为网络服务公开的衍生作品,其源代码必须在同一许可证下发布。
## 前置条件
- Python 3.11+
- 推荐安装方式(从克隆的仓库或直接从 GitHub):
pip install . # 从克隆的仓库
pip install ".[ui]" # + UI Streamlit
pip install ".[dev]" # + 测试 pytest
pip install git+https://github.com/flab75/fortilog # 无需克隆
`requirements*.txt` 文件仍适用于在没有 `pyproject.toml` 的情况下管理依赖的环境(CI 部署、手动 virtualenvs):
pip install -r requirements.txt # 仅分析引擎
pip install -r requirements-ui.txt # + UI Streamlit
pip install -r requirements-dev.txt # + 测试 pytest
## 配置(参考库)
版本库中的 `config.yaml` 已**匿名化**(示例值:`adminA`,文档 IP `203.0.113.x`,相对路径)。要分析**真实**日志,请将其复制为 `config.local.yaml`(已在 `.gitignore` 中忽略),并填入您的真实数值(管理员、WAN IP、用户、数据库的绝对路径)。然后:
```
fortilog --input ./logs --config config.local.yaml --output ./rapport
```
**从您的 `.conf` 初始化参考库**:与其手动输入所有内容,不如从一份或多份 FortiGate 备份生成一份草稿,然后在使用前**仔细复核**:
```
fortilog-confgen FW-T1.conf FW-T2.conf -o config.generated.yaml # --force pour écraser
```
推导管理员、内部网段、VPN 用户/组、IPsec peers 和 DNS;分析参数将采用项目的默认设置。`mgmt` 是启发式生成的(需要核实),且需要手动补充 `fichiers_boitier`。不会提取任何敏感信息。此功能同样在 Streamlit UI 中可用(“🧩 生成参考库”部分)。随后将其重命名为 `config.local.yaml`。
## 用法
### 图形界面 (Streamlit)
```
streamlit run app.py
```
打开浏览器:拖入您的 `.log` 文件,选择可选的 `config.yaml`,点击 **启动分析**。结果将显示在各个标签页中(高亮的异常事件、仪表板、突发检测、差异对比),并且可以直接下载 `.xlsx` 报告。
### 命令行 (CLI)
```
fortilog --input ./logs --config config.yaml --output ./rapport
```
- `--input`:包含 `.log` 文件的文件夹(一个或多个)。
- `--config`:“正常状态”参考库 + 参数(参见 `config.yaml`)。
- `--output`:生成 `rapport_fortigate.txt` 和 `rapport_fortigate.xlsx`。
## 输出结果(工作簿,12 个工作表)
0. `Rapport` — **总结**,描述结果并解释问题,区分 **[已确认]**(配置状态、数量)与 **[待确认]**(疑似)。每个部分(配置、事件、外部 IP)都会单独详细说明最严重的发现(可通过 `rapport.max_constats` 调整,默认值为 5)。在文本报告的顶部和 Streamlit UI 的“报告”标签页中也可查看。
1. `Tableau de bord` — 按设备/天统计的聚合数据(失败、成功登录、锁定、SSL-VPN、passwd_invalid、唯一 IP)。
2. `Evenements signales` — 风险事件,按严重程度(info→critique)着色,并丰富了范围/国家/ASN/信誉信息。
3. `Chaines suspectes` — 相关联的操作序列(访问→账户→数据泄露)— **待确认**。
4. `IP malveillantes` — 存在于信誉列表(threat intel)中的来源 — **待确认**。
5. `Audit config` — 对导入的 FortiGate `.conf` 的审计发现(账户、访问、自动化) — **待确认**。
6. `Comparaison config` — 与参考配置相比的差异(添加/删除/修改)以及归属人和时间的判定 — **待确认**。
7. `Sources externes` — 按流量统计的外部 IP 排名(地理/ASN 上下文) — 参见“丰富化信息”。
8. `Rafales` — 检测到的峰值(**自适应**阈值,可调整)。
9. `Differentiels` — 不同日期和不同设备之间出现/消失的实体(Prio 1 会触发警报)。
10. `Donnees unifiees` — 解析/去重后的数据(已设上限,参见限制说明)。
11. `Referentiel` — 所使用的“正常”配置。
## 检测规则(审计矩阵,12 条规则)
- 管理员从**外部**源成功登录 (critique) / 账户不在参考库中 (élevé)。
- 针对**有效账户**的暴力破解 (`passwd_invalid`, élevé) vs 不存在的账户。
- 在参考库之外建立 **SSL-VPN** 隧道 (critique)。
- **创建/修改** 管理员/SSO/API 账户 (Add=élevé ; 未知操作者=critique)。
- **潜在的恶意**账户名称(一次性模板/匿名邮箱 — SUSPICION)。
- **数据泄露**:通过 GUI 下载配置/日志。
- **触发自动化** → info(事件日志未提供具体的 action-type:需检查配置)。
- **网络**:设备向未列出的目标发出出站请求 (moyen)。
- **网络**:从 VPN pool 访问 management 接口 (élevé)。
- **UTM/app-ctrl**:被 FortiGate 阻断的应用程序 (élevé) ; `apprisk="critical"` 在白名单外未被阻断 (moyen, SUSPICION) ; 白名单外的 Proxy 类别 (élevé)。白名单可配置 (`app_ctrl_whitelist`) — 默认排除 `proxy-safebrowsing.googleapis.com` (Safe Browsing)。
- **疑似成功的暴力破解**:管理员成功登录前,在同一 IP/账户上出现 ≥ N 次失败(默认 5 次),且在特定时间窗口内(默认 60 分钟) → critique(外部源) / élevé(内部)。可配置 (`bruteforce.seuil_echecs`, `bruteforce.fenetre_minutes`)。SUSPICION,并非确凿证据。
- **异常时间段**:在工作时间外成功登录管理员(`horaires_ouvres`,默认 7h-20h)或在周末 → faible(行为 SUSPICION,可根据组织的作息调整)。
## UTM 日志类型
- `utm/app-ctrl`:由规则 R10 分析。
- `utm/ips`, `utm/webfilter`, `utm/dns`, `utm/antivirus`:被识别,标记为“(无专属规则)”,进行通用解析。在编写专属规则之前,需要这些类型的真实日志样本。
- `event/security-rating`:FortiGate 的强化基线报告 — 已整合到文本报告中(**BILAN HARDENING** 部分),不属于入侵检测,无对应的 Excel 工作表。
## 时间相关性(IoC 链)
- 在可配置的时间窗口内(默认 1 小时,`correlation.fenetre_minutes`),重建由同一**行为者**或同一 **IP** 执行的**有序操作序列** `访问 → 创建/修改账户 → 数据泄露`。完整的攻击链将被标记为 **critique**。
- 为避免噪音,攻击链仅在发生**异常访问**(外部/不在参考库中的登录,不在参考库中的 VPN)时才会启动;常规的合法管理活动不会触发此机制。
- **保障措施**:攻击链属于**时间相关性分析**,绝非确凿证据 — 始终显示为“待确认”。
## 地理/ASN 丰富信息(离线,可选)
- 每个 IP 源的**范围**(内部 / 外部 / 保留地址)— 基于 `plages_internes` 计算,**完全不需要任何数据库**。始终可用。
- 外部 IP 的 **国家 / ASN / 组织** — 仅当在 `config.yaml` 中提供了**本地数据库**时显示:
- `geo_db_path` : DB-IP Lite **Country CSV** (`start_ip,end_ip,country`, 许可证 CC-BY-4.0)。
- `asn_db_path` : iptoasn **ip2asn-v4.tsv** (`start end asn country org`, 公共领域)。
- **100% 离线**,不增加任何依赖项。请勿使用 MaxMind GeoLite2 (EULA/账户限制)。
- **下载数据库:参见 [docs/PROCEDURE_BASES_GEO.md](docs/PROCEDURE_BASES_GEO.md)。**
- **排名靠前的外部源**:按流量对外部 IP 进行排名(可揭示未被规则发现的 `name_invalid` 暴力破解),**排除已知的基础设施**(设备的 WAN/mgmt,合法的 peers/DNS)。
- **无数据库模式**:范围依然会计算,国家/ASN 列将为空,并会有诚实的说明提示。系统绝不捏造任何国家/ASN。地理信息仅作为**上下文参考**,既不是证据也不能作为洗清嫌疑的依据。
## 信誉列表(threat intel,离线,可选)
- 标记存在于**已知恶意 IP 列表**中的源 IP(`config.yaml` 中的 `reputation_lists`:`[{nom, path}]`),支持 CIDR/`.netset`/`.ipset` 格式(FireHOL, abuse.ch…)。仅限符合许可证的列表。复用了地理范围的匹配引擎。
- 输出结果:**“IP malveillantes”**工作表 + 报告部分 + 事件中的 `srcip_reputation` 列。基于真实日志测试,**4 455 个攻击者 IP** 被发现存在于 FireHOL Level 1 中。
- **仅匹配外部源**:类似 FireHOL 的列表包含保留地址(bogons,如 10/8, 192.168/16…)→ **内部** IP 绝不会被标记(避免常见的误报)。
- **待确认**:存在于列表中 = 强烈的信号,但并非确凿证据(有些列表覆盖面过广或已过时)。
## FortiGate 配置审计 (.conf)
导入一个或多个**配置备份**(`.conf`) — 通过 CLI(将其放入 `--input` 文件夹)或通过 Streamlit UI(`.conf` 拖放区)。该工具会解析 FortiGate CLI 并检查**入侵迹象**,并与参考库进行比对:
- **不在参考库中的**管理员账户(`config system admin`/`sso-admin`) → critique (SUSPICION)。
- **没有设置 trusted-host** 的管理员(可从任意 IP 访问) → élevé。
- **恶意**的管理员名称(特征匹配) → élevé ; 包含 **自动化**脚本的 `cli-script`/`webhook`(持久化后门) → élevé。
- **暴露的**管理员访问权限:`telnet`,或在 `role wan` 接口上开放 GUI/SSH → élevé。
- **被不在参考库中的账户保存的**配置(包含 `user=` 头) → moyen。
可以单独分析 `.conf` 文件(不需要日志)。所有结果均标记为**待确认**:近期新添加的合法管理员可能不在参考库中 — 这绝不是确凿的证据。
## 比较两份配置 (.conf)
将一份**参考 / 已验证**的配置与一份**当前 / 可疑**的配置进行比较:找出**变更**内容(添加/删除/修改的对象:管理员、防火墙规则、VPN、接口、路由、自动化、用户等),以及修改的**操作者**和**时间**。
```
fortilog-diff reference.conf actuel.conf --logs ./logs # --all pour toutes les sections
```
- **“操作者 / 时间”**来源于**日志**(“Object attribute configured” 事件:`cfgobj`/`user`/`action`/timestamp)。单纯的 `.conf` 文件只能提供*保存*该备份的操作者信息(`user=` 头)。如果超出了日志覆盖的时间窗口 → 显示“inconnu”(绝不捏造)。
- 哈希值/敏感信息(密码、密钥、psksecret) → 显示为**“(valeur masquée)”**。
- Streamlit UI 中同样提供此功能:**“🔁 比较两份配置**部分(拖入的日志用于行为归属判定)。
- **已整合到综合分析中**:提供一份参考配置,比较结果将纳入**全局报告**(Excel 的“Comparaison config”工作表、报告章节、总结摘要):
fortilog --input ./logs --config config.local.yaml --output ./rapport \
--ref-conf reference.conf
在 Streamlit 中:在启动分析前,将文件拖入**“Config de référence / validée”**(“🔁 Comparaison config”标签页)。
- 配置差异**并不代表**发生了入侵 — 需要进一步确认。
## 格式与解析
- `clé="valeur"`(可处理空格)。由 FortiGate 转义的**引号和反斜杠**(值内部的 `\"`, `\\`)能够被正确地反转义,且不会导致后续字段错位(已在 806 064 行真实日志上验证)。
## 对比
- 按**天**(默认)或**小时**进行聚合。
- **突发 (Rafales)**:滑动窗口(默认 1 小时),**自适应**阈值(≥ 中位数的倍数)或固定阈值 — 可在 `config.yaml` 中配置。
- 针对 6 类实体进行**差异 (Différentiel)** 对比(优先级 1→3);Prio 3(攻击 IP,目标名称)将以计数器形式汇总。
## 参考库与设备关联匹配
- 导出文件中不包含 `devname` → 设备通过 **IP**(WAN/mgmt)推导得出。对于没有设备 IP 的日志(event/user, event/vpn, traffic/forward),可通过**文件名索引**(`fichiers_boitier`)进行关联匹配。若均无法匹配:显示为 `inconnu`(诚实呈现,不盲目猜测)。
- VPN 规则:由于 VPN 用户通常来自动态的住宅 IP → 参考依据为 **user + groupe + pool**,绝不是源 IP。“外部源”检测仅适用于**管理员**登录。
## 性能与限制(实测)
- 约 0.6 秒/MB ;**内存峰值 ≈ 输入文件大小的 4 倍**(P5 阶段 1+2:列式解析 + 分析/显示列分离)。在 T1 数据(397 MB / 392 541 行)上测得:**峰值 RSS 为 1 599 Mo**,输出结果一致。
- 对于**超大文件**(> 约 600 MB 累计且可用内存较少):建议分**批次**(按天或按设备)处理。
- Excel 最大支持约 1 048 576 行 → `Donnees unifiees` 工作表将会被**截断**(参数 `max_lignes_donnees_unifiees`,默认 200 000);但**聚合分析和检测结果是基于完整的数据集**得出的。
- 未知的日志类型 → 进行通用解析并打上标记,**绝不**生成虚构的分析结果。
## 测试
纳入版本控制的 pytest 测试套件:**173 个快速测试** + **9 个基于真实日志的测试** (@slow) = **总计 182 个测试**。
```
# 快速测试 (synthetic fixtures)
python -m pytest tests/ --ignore=tests/fixtures -m "not slow"
# 真实日志测试 (需要导出在 /path/to/logs/)
python -m pytest tests/ --ignore=tests/fixtures -m "slow"
# 所有测试
python -m pytest tests/ --ignore=tests/fixtures
```
测试覆盖率:
- **parse.py**:12 个用例(引号/空格、混合、空行、真实日志行、`\"`/`\\` 转义符)。
- **ingest.py**:7 个用例(每种已知类型 + 未知类型 + 通用 UTM、文件列举)。
- **normalize.py**:6 个用例(时间戳、按 IP/文件/未知推断设备、去重)。
- **detect.py**:28 个用例(R1-R9 正反向测试,6 个 R10a/b/c 用例,3 个 R11 暴力破解用例,2 个 R12 异常时间用例)。
- **geo.py**:22 个用例(范围计算、查询 CSV/TSV/CIDR、结合信誉的地理丰富信息、降级策略、顶级来源、基础设施排除、内部保留地址排除)。
- **confaudit.py**:11 个用例(CLI 解析,C1-C6,无严重问题的干净配置,按严重程度排序)。
- **analysis.py**:13 个用例(报告章节、按规则详细列出发现、可配置的 `max_constats`、对 SUSPICION 添加 [À CONFIRMER] 标签、§3/§4 顶级事件、WAN↔暴力破解关联、告警提示、纯配置模式、空输入)。
- **confdiff.py**:12 个用例(管理员的添加/删除/修改、新规则、全局参数、敏感信息掩码、从日志提取归属人/时间、无日志情况、“保存者”头部信息、云账户→ info)。
- **compare.py**:5 个用例(聚合数据、突发、差异)。
- **correlate.py**:11 个用例(完整攻击链、良性操作、合法管理活动、操作顺序、窗口过短、步骤映射、有效 IP)。
- **validate.py**:16 个用例 — 有效配置 + 各种独立错误检测(CIDR、IP、正则表达式、阈值、关联窗口、app-ctrl 白名单、`rapport.max_constats`)。
- **ui_helpers.py**:16 个用例 — 严重性排序、指标、攻击链、时间戳格式化、颜色配置。
- **confgen.py**:6 个快速用例 + 1 个 @slow 用例(基础提取、网段+启发式 mgmt、VPN、无敏感信息、有效的 YAML、多配置文件、真实的 T1 .conf 文件)。
- **integration**:入侵场景 (≥3 个 critique)、良性场景 (0 误报)、真实 T1/T2 日志解析、基于真实 event/system 文件的检测、T1 的 app-ctrl 白名单测试。
## config.yaml 验证
`config.yaml` 文件会在**启动时进行验证**。如果出现错误:会显示明确的错误信息并停止运行 (exit 1)。主要检查项:
- 必须包含必需的键(`boitiers`, `admins_connus`, `plages_internes`, `destinations_legitimes`, `rafales`)。
- CIDR、IP 地址、正则表达式的有效性。
- 突发检测的阈值(正数值,模式为 `adaptatif` 或 `fixe`)。
## 历史测试(基于真实数据验证)
- 对重叠时间窗口内的日志进行去重(在某套真实数据中移除了 42 353 条重复记录)。
- **正向检测**:入侵场景 → 3 个 critique + 4 个 élevé。
- 在良性日志上**无误报**(如暴力破解失败 → 0 个恶意账户告警)。
- 负载扩展性:P5 后的 T1 测试 — 397 Mo / 392 541 行 → **峰值 RSS 为 1 599 Mo**(较 P5 前下降了 34%)。
标签:AMSI绕过, CISA项目, Kubernetes, Python, Streamlit, 威胁情报, 威胁检测, 安全规则引擎, 开发者工具, 插件系统, 无后门, 红队行动, 访问控制, 逆向工具, 防火墙