""" admin_panel_scanner.py — Admin Panel & Exposed Dashboard Scanner ================================================================ PHASE 1: Baseline filtering added — only reports panels that are NOT the site's generic SPA catch-all response. PHASE 2: Content-signature validation for product-specific panels. Probes a targeted wordlist of admin, monitoring, and developer dashboards that are often left exposed. More targeted than the generic directory scanner. """ import urllib.request, urllib.error from scanners.base_scanner import BaseScanner from scanners.core.signatures import matches_signature ADMIN_PATHS = [ # Generic admin panels "/admin", "/admin/", "/admin/login", "/admin/dashboard", "/_admin", "/administrator", "/admincp", "/admin1", "/admin2", "/backend", "/backend/login", "/manage", "/management", "/control", "/controlpanel", "/cp", "/cpanel", # PHP tooling "/phpmyadmin", "/pma", "/phpMyAdmin", "/phpmyadmin/", "/mysql", "/adminer", "/adminer.php", "/db", "/database", # Python/Django "/django-admin", "/django/admin", "/_admin/", # Java / Spring / JEE "/manager", "/manager/html", "/host-manager", "/console", "/actuator", "/actuator/health", "/actuator/env", "/actuator/beans", "/actuator/mappings", "/actuator/info", "/jolokia", "/jolokia/list", "/druid", "/druid/login.html", # CI/CD & DevOps "/jenkins", "/jenkins/", "/jenkins/login", "/gitlab", "/gitlab/users/sign_in", "/sonarqube", "/sonar", # Monitoring / Observability "/grafana", "/grafana/login", "/kibana", "/kibana/app/kibana", "/prometheus", "/metrics", "/_prometheus/metrics", "/jaeger", "/zipkin", # Messaging & Queues "/rabbitmq", "/rabbitmq-management", "/activemq", "/activemq/admin", "/kafka", "/kafka-ui", # Container / Cloud "/portainer", "/rancher", "/kubernetes", "/_cluster/health", "/_cat/nodes", # Elasticsearch # CMS / CRM "/wp-admin", "/wp-login.php", "/typo3", "/typo3/backend", "/joomla/administrator", "/index.php?option=com_admin", # Misc "/setup", "/setup.php", "/install", "/install.php", "/config", "/config.php", "/.env", "/server-status", "/server-info", "/status", "/info.php", "/phpinfo.php", ] ADMIN_KEYWORDS = [ "login", "dashboard", "admin", "username", "password", "sign in", "control panel", "management", "welcome back", " list: self.log("INFO", f"[AdminPanel] Probing {len(ADMIN_PATHS)} admin/dashboard paths on {self.target}...") base = self.target.rstrip("/") found = [] for path in ADMIN_PATHS: url = base + path status, body = self._probe(url) if status == 200 and body is not None: # PHASE 1: Suppress if response is the site's SPA/404 catch-all if self._is_baseline(status, body): self.log("INFO", f"[AdminPanel] SUPPRESSED (baseline match, {len(body)}b): {url}") continue # Minimum content threshold — avoid near-empty redirect bodies if len(body) < 200: self.log("INFO", f"[AdminPanel] SKIPPED (body too small, {len(body)}b): {url}") continue # PHASE 2: Product-specific signature check sig_key = _PATH_SIG_MAP.get(path) if sig_key: if not matches_signature(sig_key, body, log_fn=lambda m: self.log("INFO", m), url=url): continue else: # Generic admin check: must contain admin keywords AND a form element body_lower = body.lower() has_keyword = any(kw.lower() in body_lower for kw in ADMIN_KEYWORDS) has_form = "