Wiz Agents & Workflows are here
Vulnerability DatabaseGHSA-vg9h-jx4v-cwx2

GHSA-vg9h-jx4v-cwx2
Python vulnerability analysis and mitigation

Summary

The Unfurl web app enables Flask debug mode even when configuration sets debug = False. The config value is read as a string and passed directly to app.run(debug=...), so any non-empty string evaluates truthy. This leaves the Werkzeug debugger active by default.

Details

  • unfurl/app.py:web_app() reads debug via config['UNFURL_APP'].get('debug'), which returns a string.
  • UnfurlApp.__init__ passes that string directly to app.run(debug=unfurl_debug, ...).
  • If unfurl.ini omits debug, the default argument is the string "True".
  • As a result, debug mode is effectively always on and cannot be reliably disabled via config.

PoC

  1. Create a local unfurl.ini with debug = False under [UNFURL_APP].
  2. Run the server using unfurl_app (or python -c 'from unfurl.app import web_app; web_app()').
  3. Observe server logs showing Debug mode: on / Debugger is active!.
  4. The included PoC script security_poc/poc_debug_mode.py --spawn automates this check.

PoC Script (inline)

#!/usr/bin/env python3
"""
Unfurl Debug Mode PoC (Corrected)
================================
This PoC demonstrates that Unfurl's Flask debug mode is effectively
**always enabled by default** due to string parsing of the `debug`
config value. Even `debug = False` in `unfurl.ini` evaluates truthy
when passed to `app.run(debug=...)`.
Two modes:
1) --spawn (default): launch a local Unfurl server with debug=False
   in a temp config and inspect logs for "Debug mode: on".
2) --target: attempt a remote indicator check (best-effort; may be silent
   if no exception is triggered).
"""
import argparse
import os
import subprocess
import sys
import tempfile
import textwrap
import time
def run_spawn_check() -> None:
    repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
    ini_contents = textwrap.dedent("""
    [UNFURL_APP]
    host = 127.0.0.1
    port = 5055
    debug = False
    remote_lookups = false
    [API_KEYS]
    bitly =
    macaddress_io =
    """).strip() + "\n"
    with tempfile.TemporaryDirectory() as tmp:
        ini_path = os.path.join(tmp, 'unfurl.ini')
        with open(ini_path, 'w') as f:
            f.write(ini_contents)
        env = os.environ.copy()
        env['PYTHONPATH'] = repo_root
        cmd = [sys.executable, '-c', 'from unfurl.app import web_app; web_app()']
        proc = subprocess.Popen(
            cmd,
            cwd=tmp,
            env=env,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        # Allow server to start and emit logs
        time.sleep(2)
        proc.terminate()
        try:
            out, err = proc.communicate(timeout=2)
        except subprocess.TimeoutExpired:
            proc.kill()
            out, err = proc.communicate()
        output = (out or "") + (err or "")
    print("\n[+] Debug mode spawn check")
    print("    Config: debug = False")
    if "Debug mode: on" in output or "Debugger is active" in output:
        print("    ✅ Debug mode is ON despite debug=False (vulnerable)")
    else:
        print("    ⚠️  Debug mode not detected in logs (check output below)")
    if output.strip():
        print("\n--- server output (truncated) ---")
        print("\n".join(output.splitlines()[:15]))
        print("--- end ---")
def run_remote_probe(target: str) -> None:
    import requests
    print("\n[+] Remote debug indicator probe (best-effort)")
    print(f"    Target: {target}")
    # This app does not easily throw exceptions from user input, so
    # absence of indicators does NOT prove debug is off.
    probe_urls = [
        f"{target.rstrip('/')}/__nonexistent__",
    ]
    detected = False
    for url in probe_urls:
        try:
            resp = requests.get(url, timeout=10)
            if "Werkzeug Debugger" in resp.text or "Traceback" in resp.text:
                detected = True
                print("    ✅ Debug indicators found")
                break
        except Exception as e:
            print(f"    ⚠️  Probe failed: {e}")
    if not detected:
        print("    ⚠️  No debug indicators found (this is not definitive)")
def main():
    parser = argparse.ArgumentParser(description='Unfurl debug mode PoC (corrected)')
    parser.add_argument('--spawn', action='store_true', help='Run local spawn check (default)')
    parser.add_argument('--target', help='Target Unfurl URL for remote probe')
    args = parser.parse_args()
    if args.target:
        run_remote_probe(args.target)
    else:
        run_spawn_check()
if __name__ == '__main__':
    main()

Impact

If the service is exposed beyond localhost (bound to 0.0.0.0 or reverse-proxied), an attacker can access the Werkzeug debugger. This can disclose sensitive information and may allow remote code execution if a debugger PIN is obtained. At minimum, stack traces and environment details are exposed on errors.


SourceNVD

Related Python vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-33641HIGH7.8
  • PythonPython
  • glances
NoNoMar 30, 2026
CVE-2026-34070HIGH7.5
  • PythonPython
  • langchain-core
NoYesMar 31, 2026
CVE-2026-34231MEDIUM6.1
  • PythonPython
  • slippers
NoYesMar 30, 2026
CVE-2026-34073LOW1.7
  • PythonPython
  • python3-cryptography-debuginfo
NoYesMar 31, 2026
GHSA-955r-262c-33jcCRITICALN/A
  • PythonPython
  • telnyx
NoNoMar 30, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management