August 5, 2026
Uncovering CVE-2026–30708: Bypassing Trusted Hosts in Flask
Web application security often heavily relies on how different infrastructure layers communicate. Recently, I discovered and reported a…

By Yago Martins
2 min read
Web application security often heavily relies on how different infrastructure layers communicate. Recently, I discovered and reported a security bypass in the Werkzeug WSGI web application library (CVE-2026–30708) that allows a remote attacker to circumvent the interactive debugger's "Trusted Hosts" restrictions.
In this article, I will break down how this bypass occurs in environments using reverse proxies and explore the real-world impact of this exposure.
The Defense Mechanism
Starting in version 3.0.3, Werkzeug introduced a specific security feature to restrict access to its interactive debugger console (/console).
By default, this feature was designed to trust only requests originating from local hosts, such as .localhost and 127.0.0.1. The goal was straightforward: add a robust layer of protection to isolate the debugger, mitigating unauthorized access if a developer accidentally exposed an application running with debug=True to the public internet.
However, the validation logic fails to properly account for loopback-equivalent addresses when the application sits behind a reverse proxy.
The Root Cause
Before diving into the exploit, it is crucial to clarify the exact technical nature of this vulnerability.
During the initial analysis, one might suspect that this bypass was related to a specific HTTP header influencing cache behavior. However, after extensive testing, I confirmed that the exploit does not require any cache-related headers and has absolutely nothing to do with cache poisoning.
Instead, the vulnerability is the direct result of a routing and file permission misconfiguration.
When a reverse proxy is configured to blindly forward an attacker-controlled Host header to the backend, Werkzeug evaluates this forged value against its internal allow-list. Because values like 0.0.0.0 or even localhost are often not explicitly blocked by the underlying security logic, the external request is incorrectly processed as "local" or "trusted".
Proof of Concept (PoC)
To demonstrate this bypass, I reproduced the issue in a standard environment running a Flask/Werkzeug application behind an Nginx server.
The Nginx configuration responsible for creating the exploitable condition can be seen.
The presence of the common proxy_set_header Host $host; directive ensures that the manipulated header is passed straight to the backend without proper sanitization.
From a remote machine, executing the exploit is trivial:
curl "http://<TARGET_PUBLIC_IP>/console" -H "host: localhost"curl "http://<TARGET_PUBLIC_IP>/console" -H "host: localhost"
Instead of rejecting the packet with a 400 Bad Request or a Security Error , the server accepts it and returns the full HTML of the interactive console. Even more critically, the capture shows that the internal EVALEX SECRET is leaked in plain text within the response body.
Conclusion
CVE-2026–30708 serves as a strong reminder that relying on user-controlled headers (like Host) for critical network isolation decisions is a dangerous anti-pattern, particularly in distributed architectures.
The issue has been officially acknowledged by the Pallets team and patched in version 3.0.3+. Developers should update their dependencies immediately and ensure that environments running FLASK_DEBUG=1 or active Werkzeug debuggers are never exposed to production traffic.
By Yago Martins (Y4g0D)