Summary
Both app.py and app-exe.py start the Flask development server with debug=True bound to all network interfaces (host="0.0.0.0"). When debug mode is enabled, Werkzeug activates its interactive debugger on every unhandled exception. Any caller who can reach the port and trigger an exception gains access to a browser-based Python REPL with full server-process privileges.
Evidence
app.py:3164: app.run(host="0.0.0.0", port=8080, debug=True)
app-exe.py:1907: app.run(host="0.0.0.0", port=8000, debug=True)
Both are the canonical entrypoints committed to the repository and documented in the README.
Why this matters
The Werkzeug debugger PIN provides a weak gate at best. Documented PIN calculation bypass techniques exist for instances where an attacker can read /proc/self/cgroup or similar file paths. On a reachable host, this is unauthenticated remote code execution on the server process. The application already contains routes that are expected to throw exceptions under normal operation (fabricated-data fallbacks, missing API keys, network errors), so exception triggering is not hypothetical.
Attack or failure scenario
An attacker reaches port 8080 from the local network or a reachable deployment. They trigger an unhandled exception via any route that lacks a try/except (or via a malformed request). The Werkzeug traceback page appears with an interactive console. Using publicly documented PIN bypass techniques (reading /proc/self/cgroup, /etc/machine-id, and network interface data), the attacker computes or bypasses the PIN and executes arbitrary Python code under the server's process identity.
Root cause
The development server entrypoint was committed without a production deployment path. There is no WSGI wrapper (gunicorn, uWSGI) or debug=False override, and the README does not distinguish between development and production startup.
Recommended fix
- Remove
debug=True from all committed entrypoints immediately.
- Bind to
127.0.0.1 for local development; never bind to 0.0.0.0 in a committed entrypoint.
- Replace
app.run(...) entrypoints with a WSGI command (gunicorn app:app) as the documented production start method.
- Add a CI check that fails if
debug=True appears in any committed Python entrypoint.
Acceptance criteria
- No committed entrypoint passes
debug=True to app.run(...).
- The documented startup command uses a production WSGI server.
0.0.0.0 is not the default host binding in any committed entrypoint.
- CI fails if
debug=True is reintroduced.
Suggested labels
security, production-readiness, bug
Priority
P0
Severity
Critical — the Werkzeug interactive debugger on 0.0.0.0 is a well-documented unauthenticated RCE surface on any reachable host.
Confidence
Confirmed — both committed entrypoints contain the exact flag.
Summary
Both
app.pyandapp-exe.pystart the Flask development server withdebug=Truebound to all network interfaces (host="0.0.0.0"). When debug mode is enabled, Werkzeug activates its interactive debugger on every unhandled exception. Any caller who can reach the port and trigger an exception gains access to a browser-based Python REPL with full server-process privileges.Evidence
app.py:3164:app.run(host="0.0.0.0", port=8080, debug=True)app-exe.py:1907:app.run(host="0.0.0.0", port=8000, debug=True)Both are the canonical entrypoints committed to the repository and documented in the README.
Why this matters
The Werkzeug debugger PIN provides a weak gate at best. Documented PIN calculation bypass techniques exist for instances where an attacker can read
/proc/self/cgroupor similar file paths. On a reachable host, this is unauthenticated remote code execution on the server process. The application already contains routes that are expected to throw exceptions under normal operation (fabricated-data fallbacks, missing API keys, network errors), so exception triggering is not hypothetical.Attack or failure scenario
An attacker reaches port 8080 from the local network or a reachable deployment. They trigger an unhandled exception via any route that lacks a try/except (or via a malformed request). The Werkzeug traceback page appears with an interactive console. Using publicly documented PIN bypass techniques (reading
/proc/self/cgroup,/etc/machine-id, and network interface data), the attacker computes or bypasses the PIN and executes arbitrary Python code under the server's process identity.Root cause
The development server entrypoint was committed without a production deployment path. There is no WSGI wrapper (gunicorn, uWSGI) or
debug=Falseoverride, and the README does not distinguish between development and production startup.Recommended fix
debug=Truefrom all committed entrypoints immediately.127.0.0.1for local development; never bind to0.0.0.0in a committed entrypoint.app.run(...)entrypoints with a WSGI command (gunicorn app:app) as the documented production start method.debug=Trueappears in any committed Python entrypoint.Acceptance criteria
debug=Truetoapp.run(...).0.0.0.0is not the default host binding in any committed entrypoint.debug=Trueis reintroduced.Suggested labels
security, production-readiness, bug
Priority
P0
Severity
Critical — the Werkzeug interactive debugger on
0.0.0.0is a well-documented unauthenticated RCE surface on any reachable host.Confidence
Confirmed — both committed entrypoints contain the exact flag.