wpaudit/modules/wp_analyzer/debug_exposure.py
Huzaifa Shoukat 1c5a60dc34 feat: Major enhancements, system rename to WPAUDIT, and core improvements
This commit introduces a wide range of significant enhancements,
a system-wide rename, and core architectural improvements.

**1. System Renaming:**
   - Renamed the project from "OmegaScythe Dominator/Overlord" and "OSO" to "WPAUDIT" across all relevant files. This includes:
     - `README.md`: Updated project title, descriptions, and example file names.
     - `config/default_config.yaml`: Updated comments, `output_dir`, `report_prefix`, `default_user_agent`, and XSS fuzzing payload markers.
     - Python Modules (`main.py`, `core/utils.py`, `core/tool_runner.py`, `modules/wpscan_auditor.py`, `modules/nuclei_scanner.py`, `modules/exploit_intel/metasploit_handler.py`): Updated internal references, print statements, and default values to reflect "WPAUDIT".

**2. Extensive Module Enhancements:**
   - **wp_analyzer Suite:**
     - `sqli_checker.py`: Added sophisticated boolean-based and time-based blind SQLi detection capabilities.
     - `xss_checker.py`: Expanded XSS payloads and added heuristic URL fragment checking for potential DOM XSS vectors.
     - `admin_area_security.py`: Added checks for alternative admin paths, common admin protection plugin footprints, and .htaccess protection heuristics.
     - `advanced_user_enum.py`: Implemented oEmbed and more granular REST API user enumeration techniques.
     - `ajax_checker.py`: Added rudimentary HTML-based AJAX action discovery and a basic parameter fuzzing framework for discovered actions.
     - `config_audit.py`: Implemented heuristic checks for `WP_DEBUG` exposure, `DISALLOW_FILE_EDIT`, and `FORCE_SSL_ADMIN` status.
     - `login_page.py`: Added detection for CAPTCHA, 2FA plugin footprints, "Lost Password" & "Register" link analysis, and passive password policy hints.
     - `comment_security.py`: Added checks for comment author link `rel` attributes (nofollow, ugc, sponsored), comment moderation hints, and `wp-comments-post.php` protection.
     - `cron_checker.py`: Refined `wp-cron.php` accessibility checks, added `X-Robots-Tag` check, and informational notes on cron-related constants.
     - `debug_exposure.py`: Added checks for publicly accessible `debug.log` and Query Monitor plugin footprints.
     - `custom_endpoint_fuzzer.py`: Implemented deeper REST API endpoint discovery (listing routes within custom namespaces), consumption of AJAX actions from `ajax_checker.py`, basic unauthenticated probing, and a lightweight fuzzing capability (XSS, SQLi) for discovered custom endpoints.
     - `directory_listing.py`: Added recursive checking for `wp-content/uploads` (year/month), dynamic checking of discovered plugin/theme subdirectories, sensitive file type highlighting, and an expanded list of common directories.
     - `multisite_checker.py`: Improved multisite detection (HTML footprints like body classes, asset paths, `sunrise.php` presence) and deeper `wp-signup.php` analysis for user/site registration hints.
     - `rest_api.py` (formerly `analyze_rest_api_user_enum`): Renamed and refocused to `analyze_rest_api_general`. Now includes comprehensive listing of all REST API namespaces/routes, analysis of the API root for info disclosure, and unauthenticated checks on core endpoints (users, posts, pages, media, settings).
     - `security_headers.py`: Implemented detailed Content-Security-Policy (CSP) analysis (unsafe-inline/eval, broad sources, missing directives), deeper Strict-Transport-Security (HSTS) checks (max-age, includeSubDomains, preload), added checks for newer headers (COOP, COEP, CORP), and analysis of headers on `wp-login.php`.
     - `user_registration.py`: Added checks for CAPTCHA/anti-spam footprints, passive analysis for password strength meter indicators, heuristic checks for email verification requirements, and more detailed analysis of `wp-signup.php` in multisite contexts.
   - **Other Scanner Modules:**
     - `sqlmap_injector.py`: Added support for SQLMap tamper scripts via configuration and refined vulnerability detection from logs.
     - `nmap_scanner.py`: Enabled and refined Nmap XML parsing for better data extraction, added support for NSE script profiles, and included host script results in findings.
     - `parameter_finder.py`: Corrected `run_scan` logic for Arjun tool execution and standardized options handling.
   - **Exploit Intelligence (`exploit_intel/`):**
     - `query_builder.py`: Now leverages Nmap service information and more granular Nuclei data (tags, classification) to generate richer and more targeted exploit search queries.
     - `gatherer.py`: Handles structured query objects, includes basic query prioritization (CVEs first), stores results in a new `found_exploits_correlated` structure, and incorporates a confidence score for SearchSploit results based on query relevance.
     - `searchsploit_handler.py`: Improved JSON parsing from SearchSploit output, added extraction of "Date" and "Author" fields, and included placeholders for future exploit mirroring functionality.

**3. Core Function Improvements:**
   - **Reporting (`reporting/generator.py` & `report_template.html`):**
     - Implemented HTML report generation using Jinja2.
     - Created `reporting/report_template.html` for structured and user-friendly HTML reports.
     - Updated `main.py` to call the HTML report generation function.
   - **State Management (`core/state.py`):**
     - Integrated `orjson` for potentially faster JSON serialization/deserialization.
     - Implemented a backup mechanism (`.bak` file) before overwriting the state file during `save_state`.
     - Updated internal default paths/prefixes to align with the "WPAUDIT" renaming.
   - **Tool Checking (`core/tool_checker.py`):**
     - Implemented tool version checking against configurable minimum versions using the `packaging` library.
     - Added more specific regex patterns for version parsing for various tools.
     - Updated status reporting for tool checks to be more granular (e.g., "Found (Version OK)", "Found (Version Too Low)").
   - **Tool Runner (`core/tool_runner.py`):**
     - Improved error message for `subprocess.CalledProcessError` to include a snippet of `stderr` for better diagnostics.

**4. Configuration & Documentation:**
   - Updated `README.md` with the new "WPAUDIT" name, revised descriptions, and updated example file names.
   - Updated `config/default_config.yaml` with the "WPAUDIT" name, new report/output directory defaults, and new configuration options related to module enhancements (e.g., `exploit_intel_mirror_searchsploit_exploits`).
   - Implicitly added `Jinja2`, `orjson`, and `packaging` to `requirements.txt` dependencies.

This comprehensive suite of changes significantly advances WPAUDIT's capabilities, making it a more robust, intelligent, and user-friendly WordPress security auditing tool.
2025-05-20 00:51:10 +05:00

148 lines
9.5 KiB
Python

import re
import time
from urllib.parse import urljoin
from core.utils import sanitize_filename # Core utils needed here
from .utils import make_request # Local utils for requests
def check_wp_debug_exposure(state, config, target_url):
"""Checks for signs of WP_DEBUG being enabled and exposing errors on pages."""
module_key = "wp_analyzer"
analyzer_findings = state.get_module_findings(module_key, {})
# Ensure the specific key exists before trying to access sub-keys
if "wp_debug_exposure" not in analyzer_findings:
analyzer_findings["wp_debug_exposure"] = {"status": "Running", "exposed_on_pages": []}
debug_exposure_details = analyzer_findings["wp_debug_exposure"]
# Check config flag first
if not config.get("analyzer_check_wp_debug", True):
print(" [i] WP_DEBUG exposure check disabled in configuration.")
debug_exposure_details["status"] = "Disabled in Config"
analyzer_findings["wp_debug_exposure"] = debug_exposure_details
state.update_module_findings(module_key, analyzer_findings)
return # Exit if disabled
# Common PHP error strings that might appear if WP_DEBUG is on and errors are displayed
# Using raw strings (r"...") for regex patterns
debug_error_patterns = [
r"<b>Notice</b>:", r"<b>Warning</b>:", r"<b>Fatal error</b>:",
r"<b>Parse error</b>:", r"<b>Deprecated</b>:",
r"Call Stack", r"Stack trace",
r"in\s+.+?\s+on\s+line\s+\d+", # Matches "in /path/to/file.php on line 123"
r"WordPress\s+database\s+error" # Matches common WP DB error message
]
# Compile patterns for efficiency
compiled_patterns = [re.compile(p, re.IGNORECASE) for p in debug_error_patterns]
# Pages to check (can be expanded based on findings from other modules)
pages_to_check = [
target_url, # Check homepage
urljoin(target_url, "/wp-login.php"), # Check login page
# Check a non-existent page which might trigger 404-related errors if debug is on
urljoin(target_url, "/nonexistent-page-for-debug-test-" + sanitize_filename(str(time.time())) + ".php")
]
# Future enhancement: Add paths discovered by directory bruteforcer or sitemap scanner
# Ensure exposed_on_pages list exists
if "exposed_on_pages" not in debug_exposure_details: # For PHP error messages
debug_exposure_details["exposed_on_pages"] = []
if "exposed_debug_log_url" not in debug_exposure_details: # For debug.log
debug_exposure_details["exposed_debug_log_url"] = None
if "query_monitor_footprints" not in debug_exposure_details: # For Query Monitor
debug_exposure_details["query_monitor_footprints"] = {"detected_on_pages": [], "details": []}
exposed_php_error_pages = debug_exposure_details["exposed_on_pages"]
found_php_error_exposure = False
print(" [i] Checking for PHP error message exposure (WP_DEBUG)...")
for page_url in pages_to_check:
print(f" Checking for PHP errors on: {page_url}")
try:
response = make_request(page_url, config, method="GET", timeout=7)
if response and response.text:
page_found_php_error = False
for pattern in compiled_patterns:
if pattern.search(response.text):
print(f" [!!!] Potential PHP error (WP_DEBUG) exposure found on: {page_url}")
if page_url not in exposed_php_error_pages:
exposed_php_error_pages.append(page_url)
found_php_error_exposure = True
page_found_php_error = True
# Remediation suggestion added once later if found_php_error_exposure is true
break
# Query Monitor check on this page's content
qm_comments = re.findall(r"<!-- Query Monitor.*?-->|<!-- QM.*?-->", response.text, re.DOTALL)
qm_ids_classes = re.findall(r"id=['\"]query-monitor(-.+)?['\"]|class=['\"]qm(-.+)?['\"]", response.text)
if qm_comments or qm_ids_classes:
print(f" [!] Query Monitor footprints detected on: {page_url}")
if page_url not in debug_exposure_details["query_monitor_footprints"]["detected_on_pages"]:
debug_exposure_details["query_monitor_footprints"]["detected_on_pages"].append(page_url)
if qm_comments and "Query Monitor HTML output" not in debug_exposure_details["query_monitor_footprints"]["details"]:
debug_exposure_details["query_monitor_footprints"]["details"].append("Query Monitor HTML output (comments) found.")
if qm_ids_classes and "Query Monitor CSS IDs/classes found." not in debug_exposure_details["query_monitor_footprints"]["details"]:
debug_exposure_details["query_monitor_footprints"]["details"].append("Query Monitor CSS IDs/classes found.")
except Exception as e:
print(f" [-] Error checking page {page_url} for debug exposure: {e}")
debug_exposure_details["exposed_on_pages"] = exposed_php_error_pages
if found_php_error_exposure:
state.add_critical_alert(f"WP_DEBUG errors potentially exposed on one or more pages.")
state.add_remediation_suggestion("wp_debug_php_errors_exposed", {
"source": "WP Analyzer (Debug Exposure)",
"description": f"PHP error messages (indicative of WP_DEBUG=true and display_errors=On) found on pages like: {', '.join(exposed_php_error_pages[:2])}{'...' if len(exposed_php_error_pages)>2 else ''}. This can leak sensitive information.",
"severity": "Medium",
"remediation": "Ensure WP_DEBUG and WP_DEBUG_DISPLAY are false on production. Log errors to a private file (WP_DEBUG_LOG true, but ensure log file is not web accessible)."
})
# Check for publicly accessible debug.log
print(" [i] Checking for publicly accessible wp-content/debug.log...")
debug_log_url = urljoin(target_url, "wp-content/debug.log")
try:
log_response = make_request(debug_log_url, config, method="GET", timeout=7)
if log_response and log_response.status_code == 200 and log_response.text:
# Check if it looks like a log file (e.g., contains timestamps, PHP errors)
if re.search(r"\[\d{2}-[A-Za-z]{3}-\d{4} \d{2}:\d{2}:\d{2}(?: UTC)?\] PHP", log_response.text):
print(f" [!!!] Publicly accessible debug.log found and contains data: {debug_log_url}")
debug_exposure_details["exposed_debug_log_url"] = debug_log_url
state.add_critical_alert(f"Publicly accessible debug.log found: {debug_log_url}")
state.add_remediation_suggestion("wp_debug_log_exposed", {
"source": "WP Analyzer (Debug Exposure)",
"description": f"The WordPress debug log file (wp-content/debug.log) is publicly accessible at {debug_log_url} and contains debug information. This is a critical information leak.",
"severity": "High",
"remediation": "Ensure WP_DEBUG_LOG is true ONLY for debugging, and if so, protect the debug.log file from public web access (e.g., via .htaccess or server configuration). Delete old log files if not needed. Ideally, disable WP_DEBUG_LOG on production."
})
else:
print(f" [?] {debug_log_url} is accessible but doesn't look like a typical debug.log or is empty.")
elif log_response and log_response.status_code != 404:
print(f" [i] {debug_log_url} returned status {log_response.status_code} (not 200 or 404).")
else: # 404 or request failed
print(f" [+] No publicly accessible debug.log found at {debug_log_url} (or request failed).")
except Exception as e:
print(f" [-] Error checking for debug.log: {e}")
# Consolidate status
final_details_parts = []
if found_php_error_exposure:
final_details_parts.append(f"PHP errors exposed on {len(exposed_php_error_pages)} page(s).")
if debug_exposure_details["exposed_debug_log_url"]:
final_details_parts.append("Public debug.log found.")
if debug_exposure_details["query_monitor_footprints"]["detected_on_pages"]:
final_details_parts.append(f"Query Monitor footprints on {len(debug_exposure_details['query_monitor_footprints']['detected_on_pages'])} page(s).")
state.add_remediation_suggestion("query_monitor_exposed", {
"source": "WP Analyzer (Debug Exposure)",
"description": f"Query Monitor plugin footprints detected. If its output is visible to unauthenticated users or on production, it can leak sensitive information.",
"severity": "Low", # Can be higher if actual data is shown
"remediation": "Ensure Query Monitor (or similar debugging plugins) are configured to only display output to authenticated administrators, or are disabled on production sites."
})
if not final_details_parts:
debug_exposure_details["status"] = "Likely Not Exposed"
print(" [+] No obvious signs of common debug information exposure found.")
else:
debug_exposure_details["status"] = "Potential Exposure Found"
debug_exposure_details["details_summary"] = " ".join(final_details_parts) if final_details_parts else "No specific debug exposures identified by these checks."
analyzer_findings["wp_debug_exposure"] = debug_exposure_details
state.update_module_findings(module_key, analyzer_findings)