SecureBlockLog inStart a pentest
Vulnerability Repository
HighMisconfiguration

Debug Mode Enabled in Production

Applications running with debug mode active in production expose interactive consoles, stack traces, internal configurations, and source code to unauthenticated users.

CVSS 7.5CWE CWE-489OWASP A05:2021 — Security Misconfiguration

Description

Debug mode enabled in production (CWE-489 — Active Debug Code) is a critical misconfiguration in which a web application or framework is deployed with developer debugging features active in a production environment. Virtually every major web framework — Django, Flask, Laravel, Ruby on Rails, Express, Spring Boot, ASP.NET — provides a debug mode that enables detailed error reporting, interactive debugging interfaces, auto-reloading, and verbose logging for development convenience. When these features reach production, they expose application internals to unauthenticated users and, in the worst cases, provide interactive code execution consoles accessible over the internet.

This falls under A05:2021 — Security Misconfiguration and CWE-489. The severity (CVSS 7.5) reflects that debug mode typically causes significant information disclosure and, in frameworks like Django Werkzeug, direct remote code execution. The vulnerability is easy to introduce — a single environment variable or configuration setting — and is commonly missed when transitioning from development to production environments, especially in teams that deploy frequently or use infrastructure-as-code templates derived from development configurations.

How It Works

Werkzeug/Flask interactive debugger is the most dangerous debug mode in common use. When Flask is run with debug=True, Werkzeug enables an interactive Python console in the browser at every error page. Any user who triggers a 500 error receives a fully interactive Python shell in the browser:

# Vulnerable Flask app
from flask import Flask
app = Flask(__name__)
app.run(debug=True)  # Never in production

An attacker triggers an error and gains an interactive console:

Traceback (most recent call last):
  File "/app/views.py", line 15, in index
    result = 1 / 0

>>> import os; os.system("id")
uid=1000(www-data) gid=1000(www-data) groups=1000(www-data)
>>> open("/etc/passwd").read()
root:x:0:0:root:/root:/bin/bash...

The console is, by default, protected by a PIN — but this PIN is derived from factors like the username, the machine ID, and the app module path, all of which can be obtained from verbose error pages or /proc/self/cgroup disclosure. Tools and blog posts document PIN bypass methods for common container environments.

Django with DEBUG = True exposes:

  • Full exception pages with stack traces, source code lines, local variables, and environment variables
  • A __debug__ route showing all URL patterns
  • The DATABASES, SECRET_KEY, and other settings in the debug toolbar

Spring Boot Actuator endpoints (exposed when management.endpoints.web.exposure.include=*) include:

  • /actuator/env — all environment variables including secrets
  • /actuator/heapdump — a full JVM heap dump containing all in-memory data
  • /actuator/mappings — all URL route mappings
  • /actuator/shutdown — shut down the application remotely (if enabled)

Laravel with APP_DEBUG=true returns Ignition error pages with full stack traces and the ability to run solutions — including opening files in an IDE from the browser.

Impact

  • Remote code execution — Werkzeug's interactive debugger provides direct Python execution in the server process; equivalent interactive consoles exist in other frameworks.
  • Source code disclosure — debug error pages display source code lines, local variable values, and import paths.
  • Secret key exposure — Django DEBUG = True exposes SECRET_KEY, database passwords, and third-party API keys from settings in error page output.
  • Heap dump extraction — Spring Boot's /actuator/heapdump contains all in-memory data including session tokens, decrypted secrets, and database query results.
  • Configuration exposure — environment variables, database connection strings, and service credentials are visible in debug output.
  • Denial of service — some debug endpoints (Spring Boot /actuator/shutdown) allow remote application termination.

Detection

  1. Check framework debug indicators — trigger a 500 error (supply invalid input, request a non-existent route). A Werkzeug error page, Django yellow error page, or Laravel Ignition page confirms debug mode.
  2. Test Spring Boot Actuator endpoints — request /actuator, /actuator/env, /actuator/health, /actuator/heapdump, /actuator/mappings. An unauthenticated response to /actuator/env is a critical finding.
  3. Look for debug toolbar — Django Debug Toolbar appears as a sidebar on HTML pages; it is usually visible even to unauthenticated users when debug mode is active.
  4. Check Laravel Ignition — trigger a 500 error and check for the Ignition UI. Also check APP_DEBUG in any exposed .env files.
  5. Scan for development server indicators — look for response headers like Server: Werkzeug/2.x.x Python/3.x, which indicates the development server is running rather than a production WSGI server (gunicorn, uWSGI).
  6. Test common debug endpoints/debug, /__debug__, /console, /trace, /_ah/admin, /management.

Remediation

Set DEBUG = False (or equivalent) for all production deployments. This is a non-negotiable deployment requirement:

# Django
export DJANGO_DEBUG=False

# Flask
export FLASK_ENV=production
export FLASK_DEBUG=0

# Laravel
APP_DEBUG=false

# Spring Boot (application.properties)
spring.profiles.active=prod
management.endpoints.web.exposure.include=health,info

Use environment-specific configuration files. Never use a single configuration that sets DEBUG = True and rely on remembering to change it. Maintain separate production configuration files or enforce settings via environment variables that are explicitly set differently per environment.

Deploy with a production WSGI/ASGI server. Flask and Django development servers (flask run, python manage.py runserver) should never be used in production. Use gunicorn, uWSGI, or uvicorn behind a reverse proxy. The development server itself enables debug features regardless of the DEBUG setting in some configurations.

Secure or disable Spring Boot Actuator. Restrict actuator endpoints to authenticated admin roles, expose only health and info to unauthenticated users, and bind the management port to an internal interface only.

Include debug mode verification in deployment checks. Add an automated test that hits a known error-triggering URL and verifies the response is a generic 500 page, not a framework debug page.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping