Rootless-Ghost/LogNorm
GitHub: Rootless-Ghost/LogNorm
一个日志源规范化网关,将多源日志统一为 ECS-lite 格式并支持集成与查询。
Stars: 0 | Forks: 0
# LogNorm
Nebula Forge 检测工程平台的一部分。LogNorm 是流水线中的规范化网关:其 ECS-lite JSON 输出是被 DriftWatch、ClusterIQ、AtomicLoop 和 HuntForge 接受的共享数据格式。
## 功能
LogNorm 摄取五种源类型的原始日志文件,并将每条记录规范化为一致的 **ECS-lite** JSON 模式。规范化后的事件存储在 SQLite 中,并可导出为 JSON 或 CSV。Flask Web UI 提供上传 → 规范化 → 查看 → 导出的工作流;CLI 则处理基于文件的自动化任务。
## 源适配器
| Key | 源 | 输入格式 | 覆盖范围 |
|-----|----|----------|----------|
| `sysmon` | Microsoft Sysmon | XML (wevtutil export) | 事件 ID 1–29(进程、网络、文件、注册表、DNS、WMI、管道) |
| `wel` | Windows 事件日志 | CSV(Get-WinEvent / EndpointTriage Security.csv) | 4624/4625 登录、4688 进程、4698 任务、4720/4726 账户、4740 锁定、7045 服务 |
| `wazuh` | Wazuh 告警 | NDJSON / JSON 数组 / 单个对象 | 所有基于规则的告警;Windows 的 `data.win.*`、`syscheck`、`audit` 块 |
| `syslog` | Linux auth.log / syslog | 文本(RFC 3164 / RFC 5424 / journald) | SSH、sudo、PAM、cron、useradd、su、systemd |
| `cef` | CEF / 通用 JSON | `CEF:0|...|extensions` 或任意扁平化 JSON | 供应商无关的最佳努力字段映射 |
## 快速开始
```
# 安装
pip install flask pyyaml
# 复制配置
cp config.example.yaml config.yaml
# 运行 Web 应用(默认端口 5006)
python app.py
# 或指定端口
python app.py --port 5006
```
打开 `http://127.0.0.1:5006`
## CLI
```
# 将 Sysmon XML 标准化为 JSON(标准输出)
python cli.py --input sysmon.xml --source sysmon --pretty
# 将 WEL CSV 标准化到输出文件
python cli.py --input security.csv --source wel --output normalized.json
# 将 Wazuh 告警标准化为 CSV
python cli.py --input alerts.json --source wazuh --output out.csv --format csv
# 将 auth.log 标准化(剥离 original_log 以减小体积)
python cli.py --input /var/log/auth.log --source syslog --no-original-log
# 从标准输入读取
cat sysmon.xml | python cli.py --source sysmon --stdin
# 所有选项
python cli.py --help
```
## API 端点
| 方法 | 路径 | 描述 |
|------|------|------|
| `GET` | `/api/health` | 健康检查 — `{"status":"ok","tool":"lognorm","version":"1.0.0"}` |
| `GET` | `/api/sources` | 列出支持的类型与描述 |
| `POST` | `/api/normalize` | 规范化单条记录(JSON 主体) |
| `POST` | `/api/normalize/batch` | 规范化文件(multipart)或记录列表(JSON) |
| `GET` | `/api/records` | 列出已存储的事件(分页 + 过滤) |
| `GET` | `/api/record/` | 按 UUID 获取单条已存储事件 |
| `GET` | `/api/sessions` | 列出规范化会话 |
| `GET` | `/api/export` | 导出为 JSON 或 CSV(`?format=json|csv&session_id=...`) |
| `DELETE` | `/api/records` | 清除所有已存储记录 |
### POST /api/normalize
```
POST /api/normalize
{"source_type": "sysmon", "raw": "... "}
→ {"success": true, "event": {}, "session_id": "uuid"}
```
### POST /api/normalize/batch
```
// JSON body
POST /api/normalize/batch
{"source_type": "wazuh", "raw": "...full file content..."}
{"source_type": "syslog", "records": ["Apr 3 ...", "Apr 3 ..."]}
// File upload (multipart/form-data)
POST /api/normalize/batch
file= source_type=sysmon
→ {"success": true, "events": [{}, ...], "failed": 0,
"total": 45, "session_id": "uuid", "filename": "sysmon.xml"}
```
## ECS-lite 模式参考
**模式版本:1.0** — 必需字段为 `event.id`、`event.created`、`event.source_type`、`log.source_type`。其余字段均为可选,空值时省略。
### 顶层
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `schema_version` | 字符串 | 是 | 始终为 `"1.0"` |
| `tags` | 数组 | 是 | 自由格式标签(例如 `T1059.001`、`atomic-test`) |
### 事件
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `event.id` | 字符串 | **是** | UUID v4 — 该规范化事件的唯一标识符 |
| `event.created` | ISO8601 | **是** | 事件创建时间(UTC) |
| `event.source_type` | 字符串 | **是** | 适配器:`sysmon` \| `wel` \| `wazuh` \| `syslog` \| `cef` |
| `event.category` | 数组 | 否 | ECS 类别:`process` \| `network` \| `file` \| `registry` \| `authentication` \| `iam` \| `driver` |
| `event.type` | 数组 | 否 | ECS 类型:`start` \| `end` \| `creation` \| `deletion` \| `access` \| `change` \| `connection` \| `protocol` |
| `event.action` | 字符串 | 否 | 源日志中的人类可读动作 |
| `event.outcome` | 字符串 | 否 | `success` \| `failure` \| `unknown` |
| `event.severity` | 整数 | 否 | 规范化后的 0–100 严重性 |
| `event.original_event_id` | 字符串 | 否 | 源事件 ID(例如 `1`、`4624`) |
### 主机
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `host.name` | 字符串 | 否 | 源端点的主机名 |
| `host.hostname` | 字符串 | 否 | 与 `host.name` 相同 |
| `host.ip` | 数组 | 否 | 与主机关联的 IP 地址 |
| `host.os.type` | 字符串 | 否 | `windows` \| `linux` \| `macos` |
| `host.os.name` | 字符串 | 否 | 完整操作系统名称 |
### 进程
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `process.pid` | 整数 | 否 | 进程 ID |
| `process.ppid` | 整数 | 否 | 父进程 ID |
| `process.name` | 字符串 | 否 | 镜像名称(例如 `powershell.exe`) |
| `process.executable` | 字符串 | 否 | 可执行文件的完整路径 |
| `process.command_line` | 字符串 | 否 | 包含参数的完整命令行 |
| `process.hash.md5` | 字符串 | 否 | 进程镜像的 MD5(小写十六进制) |
| `process.hash.sha256` | 字符串 | 否 | 进程镜像的 SHA-256(小写十六进制) |
| `process.parent.pid` | 整数 | 否 | 父进程 PID |
| `process.parent.name` | 字符串 | 否 | 父镜像名称 |
| `process.parent.executable` | 字符串 | 否 | 父可执行文件的完整路径 |
| `process.parent.command_line` | 字符串 | 否 | 父命令行 |
### 网络
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `network.direction` | 字符串 | 否 | `ingress` \| `egress` \| `internal` |
| `network.transport` | 字符串 | 否 | `tcp` \| `udp` \| `icmp` |
| `network.destination.ip` | 字符串 | 否 | 目标 IP |
| `network.destination.port` | 整数 | 否 | 目标端口 |
| `network.destination.domain` | 字符串 | 否 | 目标主机名 / 域名 |
| `network.source.ip` | 字符串 | 否 | 源 IP |
| `network.source.port` | 整数 | 否 | 源端口 |
### 文件
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `file.path` | 字符串 | 否 | 完整文件路径 |
| `file.name` | 字符串 | 否 | 不含目录的文件名 |
| `file.extension` | 字符串 | 否 | 不含前导点的扩展名 |
| `file.hash.md5` | 字符串 | 否 | MD5(小写十六进制) |
| `file.hash.sha256` | 字符串 | 否 | SHA-256(小写十六进制) |
| `file.size` | 整数 | 否 | 以字节为单位的文件大小 |
### 注册表
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `registry.path` | 字符串 | 否 | 完整注册表路径 |
| `registry.key` | 字符串 | 否 | 注册表键名 |
| `registry.value.name` | 字符串 | 否 | 值名称 |
| `registry.value.type` | 字符串 | 否 | 值类型(`REG_SZ`、`REG_DWORD` 等) |
| `registry.value.data` | 字符串 | 否 | 值数据 |
### 用户
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `user.name` | 字符串 | 否 | 账户用户名 |
| `user.domain` | 字符串 | 否 | 域或工作组 |
| `user.id` | 字符串 | 否 | SID 或 UID |
### 日志
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `log.source_type` | 字符串 | **是** | 使用的适配器 |
| `log.source_tool` | 字符串 | 否 | 生成原始日志的工具 |
| `log.original_event_id` | 字符串 | 否 | 原始事件标识符 |
| `log.original_log` | 对象 | 否 | 保留原始来源记录 verbatim |
## Nebula-Forge 集成
**nebula-dashboard config.yaml** — 添加到 `tools:` 块:
```
lognorm:
label: "LogNorm"
url: "http://127.0.0.1:5006"
health_path: "/api/health"
description: "Log source normalizer — Sysmon / WEL / Wazuh / syslog / CEF → ECS-lite"
category: "Normalize"
```
**detection-pipeline / purple-loop config.yaml:**
```
lognorm_url: "http://127.0.0.1:5006"
```
## 架构
```
LogNorm/
├── app.py Flask web app + all API endpoints
├── cli.py Standalone CLI
├── config.example.yaml Configuration template
├── adapters/
│ ├── base.py BaseAdapter — parse() + parse_file()
│ ├── sysmon.py Sysmon XML (EventIDs 1–29)
│ ├── wel.py Windows Event Log CSV
│ ├── wazuh.py Wazuh JSON / NDJSON
│ ├── syslog.py Linux auth.log / syslog / journald
│ └── cef.py CEF + generic JSON
├── core/
│ ├── engine.py NormalizationEngine — dispatch + fallback
│ ├── models.py make_ecs_event() factory + helpers
│ ├── schema.py Field reference + source descriptions
│ └── storage.py SQLite — sessions + events tables
├── static/css/style.css Nebula Forge dark theme
├── static/js/main.js Upload, normalize, render, pagination
└── templates/
├── base.html Sidebar layout
├── index.html Upload + normalize UI
├── records.html Browse stored events
└── schema.html ECS-lite field reference
```
**回退模式**(与 detection-pipeline / ir-chain 一致):如果 SQLite 写入失败,规范化的事件将保存到 `./output/fallback___.json`,以免丢失数据。
## 家庭实验室验证
已针对以下环境验证:
- **Wazuh** 4.14.4 在 `` — Wazuh 适配器目标为此告警格式
- **Windows 代理** — Sysmon(SwiftOnSecurity 配置)+ EndpointTriage Security.csv
- **Linux 代理** — auth.log / journald 输出
- **Splunk(Linux 代理)** — WEL CSV 导出
## 许可证
本项目根据 MIT 许可证授权 — 详细信息请参阅 [LICENSE](LICENSE) 文件。
由 [Rootless-Ghost](https://github.com/Rootless-Ghost) 构建
属于 **Nebula Forge** 安全工具套件的一部分。
标签:AMSI绕过, CEF, CLI 自动化, CSV 导出, ECS-lite, Flask Web UI, JSON 导出, Log 规范化, Nebula Forge, SQLite 存储, syslog, Sysmon, Wazuh, Windows 事件日志, 入侵检测系统, 威胁检测, 字段映射, 安全数据湖, 安全日志, 日志压缩, 日志可视化, 日志处理, 日志归一化, 日志格式转换, 日志清洗, 日志管道, 日志管道化, 日志网关, 日志采集, 日志集成, 检测工程平台, 逆向工具