BearlyStable/Simple-GPO-Explorer
GitHub: BearlyStable/Simple-GPO-Explorer
一款基于 Web 的 GPO 备份归档分析工具,主要用于解析 SYSVOL 导出文件并自动恢复组策略偏好设置中的加密凭据。
Stars: 0 | Forks: 0
# Simple GPO Explorer
一个基于 Web 的组策略对象 (GPO) 备份归档分析器。
上传 SYSVOL 样式的 `.zip` 导出文件(包含 `GPT.INI` 的 `{GUID}` 目录),然后即可浏览、搜索并检查其中包含的每一个 GPO —— 安全模板、注册表策略、客户端扩展以及任何可恢复的组策略偏好设置凭据 —— 所有操作都在由 SQLite 支持的本地暗色模式界面中完成。
## 功能
- **上传与解析** GPO 备份 `.zip` 归档(支持拖放或文件选择器)—— 预期为 SYSVOL 样式的 `{GUID}` 目录布局,每个目录需包含 `GPT.INI`;目录会被递归发现,因此直接压缩整个 `Policies` 文件夹的 zip 包也可直接使用
- 对归档中发现的每个 GPO 进行 **深度工件解析**:
- `GPT.INI` —— GPO 版本(拆分为计算机/用户版本号)
- `Group Policy\GPE.INI` —— 已配置的客户端扩展,根据精选的知名扩展 GUID 列表进行解析(注册表、安全、文件夹重定向、本地用户和组等)
- `SecEdit\GptTmpl.inf`(计算机和用户)—— UTF-16LE 安全模板,解析为有序的节/键
- `Registry.pol`(计算机和用户)—— 二进制注册表策略格式,完全解码(`REG_SZ`、`REG_EXPAND_SZ`、`REG_DWORD`、`REG_QWORD`、`REG_MULTI_SZ`、`REG_BINARY`)
- **组策略偏好设置凭据恢复** —— 每一个 `Preferences\*.xml` 文件(`Groups.xml`、`Drives.xml`、`ScheduledTasks.xml`、`Services.xml` 等)都会被扫描以查找 `cpassword` 属性,并使用微软公布的 AES 密钥(MS14-025 / KB2962486)进行解密
- 任何单个工件上的解析失败都会被记录为特定 GPO 的警告,而不会中断整个上传过程
- **归档集** —— 每次上传都会作为其独立的归档集存储在 SQLite 中;可从下拉菜单中切换以前上传的归档,或彻底删除某个归档
- **GPO 表格** 带有实时状态栏(显示的 GPO 数量、包含凭据发现的 GPO 数量、已解密的凭据总数),并包含名称、GPT 版本、扩展数量、计算机/用户策略存在状态、凭据发现以及备注等列
- **搜索栏** 支持自由文本加上 `key:value` 运算符(请参阅 [搜索运算符](#search-operators));多个 token 以 AND 组合,任何运算符都可以使用 `-` 进行否定
- 每一个 GPO 的 **GPO 详细视图** 解析工件:
- GPT.INI 摘要、扩展列表、SecEdit 节、解码后的 Registry.pol 条目
- **凭据发现** 部分会高亮显示任何已解密的 `cpassword` 值,并附带源文件和账户名
- **文件浏览器** —— GPO 内部的每个文件都会被列出并可供查看:文本和 XML 会内联呈现(XML 经过格式化美化,大文件会被截断),二进制文件会通过扩展名/内容嗅探进行识别,且绝不会转储其原始字节
- 该 GPO 的任何解析错误都会被单独展示出来
- **显示名称映射** —— 上传一个包含 `{GUID}` 单元格和显示名称列的 markdown 表格文件(例如从 GPMC/RSOP 导出),以便为匿名的 GPO 添加标签;列的顺序和表头并不重要,只要每行有一个单元格呈 GUID 形式即可
- **备注** —— 每个 GPO 提供一个自由文本备注字段,并持久化存储在数据库中
- **标签** —— 每个 GPO 可附加任意标签,支持持久化存储并可通过 `tag:` 运算符进行搜索
- **暗色模式 UI**,无外部 CDN 依赖 —— 所有内容(样式、脚本)均在本地提供
## 要求
- Python 3.11 或更高版本
- Pip / venv
## 安装
```
# 1. Clone the repository
git clone https://github.com/BearlyStable/Simple-GPO-Explorer.git
cd Simple-GPO-Explorer
# 2. 创建并激活虚拟环境
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
# 3. 安装依赖
pip install -r src/requirements.txt
```
在 macOS/Linux 上,你可以使用自带的 `Makefile` 来代替上述步骤:
```
make setup # creates src/.venv and installs dependencies
make run # runs the app (implies setup)
```
## 运行应用程序
```
cd src
python app.py
```
服务器在 debug 模式下于 **http://localhost:4000** 启动。
用于生产环境部署(与 Docker 镜像运行的内容相匹配):
```
pip install gunicorn
cd src
gunicorn --bind 0.0.0.0:5000 --workers 1 --threads 4 --timeout 120 app:app
```
实用的环境变量:
| 变量 | 默认值 | 用途 |
|---|---|---|
| `SIMPLE_GPO_EXPLORER_SECRET_KEY` | `dev-secret-key-change-me` | Flask session/flash 签名密钥 |
| `SIMPLE_GPO_EXPLORER_DB` | `/simple_gpo_explorer.sqlite3` | SQLite 数据库路径 |
| `SIMPLE_GPO_EXPLORER_UPLOAD_DIR` | `/uploads` | 存储上传归档的目录 |
## Docker
### 拉取已发布的镜像
GitHub Actions 工作流(`.github/workflows/docker-publish.yml`)构建镜像并将其发布到 GitHub Container Registry:
- 每次推送到 `main` 分支都会发布 `ghcr.io/bearlystable/simple-gpo-explorer:latest`
- 每个发布的 GitHub Release 都会发布一个带版本标签的镜像,例如 `ghcr.io/bearlystable/simple-gpo-explorer:1.0.0`(以及 `1.0`)
```
docker pull ghcr.io/bearlystable/simple-gpo-explorer:latest
```
### 在本地构建并运行
```
cd docker
docker compose up -d --build
```
随后即可通过 **http://localhost:5000** 访问该应用程序。
### 配置
将 `docker/.env.example` 复制到 `docker/.env` 以覆盖默认值(`docker compose` 会自动加载它):
```
cp docker/.env.example docker/.env
```
| 变量 | 默认值 | 用途 |
|---|---|---|
| `SIMPLE_GPO_EXPLORER_SECRET_KEY` | `dev-secret-key-change-me` | Flask session/flash 签名密钥 |
| `SIMPLE_GPO_EXPLORER_PORT` | `5000` | 发布 Web UI 的主机端口 |
### 持久化数据
SQLite 数据库和每个上传的 `.zip` 归档都保存在容器内的 `/app/instance` 目录下,并由名为 `simple_gpo_explorer_data` 的命名卷支持。停止、移除或重建容器都**不会**影响此卷:
```
docker compose down # data preserved
docker compose down -v # -v also deletes simple_gpo_explorer_data — be sure first
```
### 将镜像迁移到离线机器
```
make release # builds the image and exports it to dist/simple-gpo-explorer-.tar.gz
```
目标机器只需要 tarball 以及 `docker/` 目录(不需要源码树、registry 或互联网访问):
```
docker load -i simple-gpo-explorer-.tar.gz
docker compose -f docker/docker-compose.yml up -d
```
### 备份与恢复
将命名卷备份为 tarball:
```
docker run --rm -v simple_gpo_explorer_data:/data -v "$PWD":/backup alpine \
tar czf /backup/simple-gpo-explorer-backup.tar.gz -C /data .
```
通过将 tarball 解压回卷中来还原它,然后运行 `docker compose up -d`。
有关完整的操作步骤,请参阅 [docker/README.md](docker/README.md)。
## 用法
1. 在浏览器中打开 **http://localhost:4000**(在 Docker 下为 **:5000**)。
2. 点击右上角的 **Upload GPO Archive**。
3. 拖放(或浏览找到)一个 `.zip` 导出文件 —— 即 SYSVOL 样式的 `{GUID}` 目录布局,每个目录需包含 `GPT.INI`(例如压缩了域控制器的 `SYSVOL\\Policies` 文件夹的 zip 包)。
4. 归档中的每个 GPO 都会被解析并存储为一个新的 **archive set**;使用顶部栏的下拉菜单可在不同归档集之间切换。
5. 使用 **search bar** 按名称、GUID、版本、策略范围、凭据发现、备注或标签进行筛选。
6. 点击任意行以打开 **GPO detail view**,其中包含每个已解析的工件、文件浏览器、备注和标签。
7. 可选择点击 **Display Names** 上传 markdown GUID → 名称映射表,为该归档集中的 GPO 添加标签。
### 归档布局示例
```
{31B2F340-016D-11D2-945F-00C04FB984F9}/
├── GPT.INI
├── Group Policy/
│ └── GPE.INI
├── MACHINE/
│ ├── Microsoft/Windows NT/SecEdit/GptTmpl.inf
│ ├── Registry.pol
│ └── Preferences/Groups/Groups.xml
└── USER/
├── Registry.pol
└── Preferences/Drives/Drives.xml
```
同一个 zip 中的多个 `{GUID}` 目录都会被作为同一个归档集的一部分发现并解析。
## 项目结构
```
Simple-GPO-Explorer/
├── LICENSE
├── Makefile # setup / run / docker-* / release targets
├── .dockerignore # Applies to the docker build context (repo root)
├── src/
│ ├── app.py # Flask application — routes, upload handling, REST API
│ ├── requirements.txt # Python dependencies
│ ├── gpo_analyzer/ # Parsing engine (no Flask dependency)
│ │ ├── analyzer.py # Orchestrates parsing of a single GPO directory
│ │ ├── extractor.py # Zip upload/extraction, zip-slip protection, GPO discovery
│ │ ├── gpt_ini.py # GPT.INI parser
│ │ ├── gpe_ini.py # GPE.INI / client-side extension parser
│ │ ├── secedit.py # SecEdit\GptTmpl.inf parser
│ │ ├── registry_pol.py # Binary Registry.pol parser
│ │ ├── preferences.py # Group Policy Preferences cpassword scanner/decryptor
│ │ ├── file_inspector.py # Binary/text detection for the in-browser file viewer
│ │ ├── displayname_md.py # Markdown GUID → display-name table parser
│ │ ├── pathutils.py # Case-insensitive path resolution
│ │ └── db.py # SQLite schema, migrations, archive/GPO persistence
│ ├── templates/
│ │ ├── base.html # Shared layout, upload/display-name modals
│ │ ├── index.html # Archive set / GPO table page
│ │ └── detail.html # GPO detail page
│ ├── static/
│ │ ├── style.css # All custom styles (dark theme, badges, layout)
│ │ └── app.js # All application logic (API calls, search, rendering)
│ └── instance/ # Created at runtime — SQLite DB and stored upload archives
└── docker/
├── Dockerfile # Production image (gunicorn, python:3.12-slim)
├── docker-compose.yml # Compose file with a named volume for persistence
├── .env.example # Template for docker/.env overrides
└── README.md # Extended Docker usage, backup/restore, offline transfer
```
## API 端点
| 方法 | 路径 | 描述 |
|--------|------|-------------|
| `GET` | `/` | 主 Web UI(归档集 / GPO 表格) |
| `GET` | `/gpo//` | GPO 详情页 |
| `GET` | `/api/archive-sets` | 列出所有归档集 |
| `POST` | `/api/archive-sets` | 上传并解析 GPO 备份 `.zip`(multipart 字段:`gpo_zip`) |
| `GET` | `/api/archive-sets/` | 获取一个归档集的元数据 |
| `DELETE` | `/api/archive-sets/` | 删除一个归档集、其包含的 GPO 及其存储的归档文件 |
| `GET` | `/api/archive-sets//gpos` | 列出归档集中 GPO 的摘要信息 |
| `GET` | `/api/archive-sets//gpos/` | 获取一个 GPO 的完整解析详情 |
| `PATCH` | `/api/archive-sets//gpos//comment` | 设置或清除 GPO 的备注文本 |
| `PATCH` | `/api/archive-sets//gpos//tags` | 替换 GPO 的标签列表(`{"tags": [...]}`) |
| `GET` | `/api/archive-sets//gpos//files/` | 查看 GPO 中单个文件的内容(支持二进制安全;直接从存储的 zip 中重新读取) |
| `POST` | `/api/archive-sets//display-names` | 上传 markdown GUID → 显示名称映射表(multipart 字段:`display_names_md`) |
## 搜索运算符
搜索栏接受纯文本(匹配显示名称、GUID、备注和标签)以及结构化的 `key:value` 运算符。多个 token 会以 **AND** 组合。在任何 token 前加上 `-` 可对其进行否定。`*` 字符作为通配符使用。
| 运算符 | 描述 | 示例 |
|----------|-------------|---------|
| `displayname:` | 显示名称 | `displayname:*policy*` |
| `guid:` | GPO GUID | `guid:31B2F340*` |
| `version:` | 精确的 GPT.INI 版本号 | `version:3` |
| `machine:yes/no` | 包含计算机端策略 | `machine:yes` |
| `user:yes/no` | 包含用户端策略 | `user:no` |
| `creds:yes/no` | 包含已解密的 GPP 凭据 | `creds:yes` |
| `notes:yes/no` | 包含备注 | `-notes:yes` |
| `comment:` | 备注文本 | `comment:*legacy*` |
| `tag:value` | 带有特定标签的 GPO | `tag:reviewed`, `-tag:stale` |
**示例:**
```
# GPOs with recoverable credentials
creds:yes
# Named, machine-only policies without a review comment
displayname:*policy* machine:yes user:no -notes:yes
# Everything tagged "legacy"
tag:legacy
```
## 许可证
Simple GPO Explorer 采用 [GNU GPLv3](LICENSE) 许可。
### 第三方软件
本项目构建于以下开源软件之上,并在设置/构建时通过 `pip` 安装(本仓库并未以源码形式重新分发其中的任何一项):
| 项目 | 许可证 | 作用 |
|---|---|---|
| [Flask](https://github.com/pallets/flask) | BSD-3-Clause | Web 框架 |
| [Werkzeug](https://github.com/pallets/werkzeug) | BSD-3-Clause | WSGI 工具包(Flask 依赖) |
| [gunicorn](https://github.com/benoitc/gunicorn) | MIT | 生产级 WSGI 服务器(Docker 镜像) |
| [PyCryptodome](https://github.com/Legrandin/pycryptodome) | BSD-2-Clause | 对组策略偏好设置 `cpassword` 值进行 AES 解密 |
以上各项均采用宽松的开源许可证,因此在 GPLv3 下分发本项目不存在 copyleft 兼容性问题。
标签:凭据提取, 数据可视化, 组策略, 请求拦截, 调试辅助, 逆向工具