Test case 02 / 26FAIL
Active Debug Code - Application Server - Werkzeug Debugger
Lab environment — not a production system
Screenshots and raw traffic are not translated; text within them may be in another language.
vuln-bank/Evidence/[FAIL] Active Debug Code - Application Server - Werkzeug Debugger
01 — Notes
Test notes
Vulnerability
[FAIL] Active Debug Code - Application Server - Werkzeug Debugger
Description
The application runs on the Flask development server in debug mode (app.run(debug=True)), so the interactive Werkzeug console is served without authentication at /console. That page also exposes the debugger's SECRET value. Response headers disclose the exact Werkzeug and Python versions.
Impact
Medium: the Werkzeug debugger console is designed to execute arbitrary Python code on the server. The endpoint is reachable without authentication and its SECRET value is readable, so only the debugger PIN stands between an attacker and command execution. That PIN is derived from host attributes and can be computed if the attacker obtains a file-read primitive, making this finding a path to remote code execution. In addition, the development server is not intended for production and the precise version banner makes matching public exploits easier.
Honest testing note: code execution was NOT achieved. The debugger PIN is active and in this test was only readable via container logs, which is not a path reachable by an external attacker. Attempts to derive the PIN via SSRF also failed because the requests library does not support the file:// scheme. This finding is therefore reported as debugger exposure, not as a proven RCE.
CWE
CWE-489: Active Debug Code
OWASP
A05:2021 - Security Misconfiguration
Scenario
Exploitation: an unauthenticated GET request to /console -> HTTP 200 serves the interactive Werkzeug console along with the debugger SECRET value. Supporting evidence: response headers on any route disclose Werkzeug/2.0.1 and Python/3.9.25 precisely.
Endpoint
GET http://127.0.0.1:5001/console
Affected feature
Application server configuration (Flask development server, debug mode)
Remediation
- Disable debug mode in every environment other than a developer workstation; ensure debug=False in production.
- Run the application behind a production WSGI server (gunicorn/uWSGI) and do not use the Flask development server.
- Block /console at the reverse proxy as defense in depth.
- Hide or mask the Server header so it does not disclose exact versions.
Proof of concept
1) (Screenshot 1) EXPLOITATION: GET /console without authentication -> HTTP 200 serves the interactive Werkzeug console along with the debugger SECRET value.
2) (Screenshot 2) SUPPORTING: response headers disclose Server: Werkzeug/2.0.1 Python/3.9.25, confirming the development server is running in debug mode.
View source note (English)
Vulnerability:
[FAIL] Active Debug Code - Application Server - Werkzeug Debugger
Description:
The application runs on the Flask development server in debug mode (app.run(debug=True)), so the interactive Werkzeug console is served without authentication at /console. That page also exposes the debugger's SECRET value. Response headers disclose the exact Werkzeug and Python versions.
Impact:
Medium: the Werkzeug debugger console is designed to execute arbitrary Python code on the server. The endpoint is reachable without authentication and its SECRET value is readable, so only the debugger PIN stands between an attacker and command execution. That PIN is derived from host attributes and can be computed if the attacker obtains a file-read primitive, making this finding a path to remote code execution. In addition, the development server is not intended for production and the precise version banner makes matching public exploits easier.
Honest testing note: code execution was NOT achieved. The debugger PIN is active and in this test was only readable via container logs, which is not a path reachable by an external attacker. Attempts to derive the PIN via SSRF also failed because the requests library does not support the file:// scheme. This finding is therefore reported as debugger exposure, not as a proven RCE.
CWE:
CWE-489: Active Debug Code
OWASP:
A05:2021 - Security Misconfiguration
Scenario:
Exploitation: an unauthenticated GET request to /console -> HTTP 200 serves the interactive Werkzeug console along with the debugger SECRET value. Supporting evidence: response headers on any route disclose Werkzeug/2.0.1 and Python/3.9.25 precisely.
Endpoint:
GET http://127.0.0.1:5001/console
Affected Feature:
Application server configuration (Flask development server, debug mode)
Remediation:
- Disable debug mode in every environment other than a developer workstation; ensure debug=False in production.
- Run the application behind a production WSGI server (gunicorn/uWSGI) and do not use the Flask development server.
- Block /console at the reverse proxy as defense in depth.
- Hide or mask the Server header so it does not disclose exact versions.
POC:
1) (Screenshot 1) EXPLOITATION: GET /console without authentication -> HTTP 200 serves the interactive Werkzeug console along with the debugger SECRET value.
2) (Screenshot 2) SUPPORTING: response headers disclose Server: Werkzeug/2.0.1 Python/3.9.25, confirming the development server is running in debug mode.
02 — Screenshots
Screenshots (2)
03 — Requests and responses
HTTP captures
Some credential values are masked in this public HTML view. This does not mean every sensitive value or linked file has been redacted.
CAPTURE 01 / 1
GET /console HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1909
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 03:12:57 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Console // Werkzeug Debugger</title>
<link rel="stylesheet" href="?__debugger__=yes&cmd=resource&f=style.css"
type="text/css">
<!-- We need to make sure this has a favicon so that the debugger does
not accidentally trigger a request to /favicon.ico which might
change the application's state. -->
<link rel="shortcut icon"
href="?__debugger__=yes&cmd=resource&f=console.png">
<script src="?__debugger__=yes&cmd=resource&f=debugger.js"></script>
<script type="text/javascript">
var TRACEBACK = -1,
CONSOLE_MODE = true,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "wQNdhu5XU86XonfW6ITo";
</script>
</head>
<body style="background-color: #fff">
<div class="debugger">
<h1>Interactive Console</h1>
<div class="explanation">
In this console you can execute Python expressions in the context of the
application. The initial namespace was created by the debugger automatically.
</div>
<div class="console"><div class="inner">The Console requires JavaScript.</div></div>
<div class="footer">
Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
friendly Werkzeug powered traceback interpreter.
</div>
</div>
<div class="pin-prompt">
<div class="inner">
<h3>Console Locked</h3>
<p>
The console is locked and needs to be unlocked by entering the PIN.
You can find the PIN printed out on the standard output of your
shell that runs the server.
<form>
<p>PIN:
<input type=text name=pin size=14>
<input type=submit name=btn value="Confirm Pin">
</form>
</div>
</div>
</body>
</html>
CAPTURE 02 / 2
GET /nonexistent-path-for-server-banner HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
HTTP/1.1 404 NOT FOUND
Content-Type: text/html; charset=utf-8
Content-Length: 232
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 03:12:58 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>