Your firewall's front door is a screen door — attackers are walking in and minting admin accounts without credentials
CVE-2025-64446 is a relative path traversal (CWE-23) in Fortinet FortiWeb, the company's dedicated Web Application Firewall appliance. The flaw lives in the management-plane GUI API: an attacker sends a crafted POST to /api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi, escaping the REST API sandbox and landing directly on the fwbcgi binary that processes admin-plane commands. Because fwbcgi trusts the HTTP_CGIINFO header's Base64-encoded JSON (containing username, profile name, VDOM, and login name) without validating the request came through the authenticated session handler, an unauthenticated remote attacker can impersonate the built-in admin and create a brand-new administrator account with prof_admin privileges, 0.0.0.0/0 trusted-host scope, and an attacker-chosen password. This works against FortiWeb 7.0.0–7.0.11, 7.2.0–7.2.11, 7.4.0–7.4.9, 7.6.0–7.6.4, and 8.0.0–8.0.1. Fixed versions are 7.0.12+, 7.2.12+, 7.4.10+, 7.6.5+, and 8.0.2+.
Fortinet scored this CVSS 9.8 CRITICAL — and for once, the vendor severity is entirely correct and arguably understates operational risk. The CVSS vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H accurately reflects that this is zero-click, zero-auth, network-reachable. But CVSS doesn't capture that the target is a perimeter security appliance that is internet-facing by design, that exploitation was confirmed in the wild over a month before formal disclosure, that a Metasploit module ships turnkey, that an initial-access broker ('k0t') has already harvested 70,000+ credentials from FortiWeb and related appliances and is selling access to Qilin and Inc ransomware groups, or that 8,163+ unique IPs were scanning for this bug by January 2026. The vendor label matches; real-world urgency exceeds it.
5 steps from start to impact.
Enumerate internet-facing FortiWeb management planes
- FortiWeb management interface is reachable from the internet
- Best-practice deployments restrict management to OOB/management VLANs, reducing the attack surface — but thousands remain exposed
Send path-traversal POST to escape the API sandbox
POST /api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi to the FortiWeb management interface. The %3f (URL-encoded ?) tricks the API router into treating the rest of the path as a query string, while the underlying file-resolution logic follows the ../ traversal literally, landing on the /cgi-bin/fwbcgi binary outside the intended API namespace. No authentication token, cookie, or session is required. The Metasploit module admin/http/fortinet_fortiweb_create_admin automates this step.- Target runs an affected FortiWeb version (7.0.x ≤ 7.0.11, 7.2.x ≤ 7.2.11, 7.4.x ≤ 7.4.9, 7.6.x ≤ 7.6.4, 8.0.x ≤ 8.0.1)
- Patched versions (8.0.2+, 7.6.5+, etc.) return HTTP 403, stopping the chain dead
%3f/../ or cgi-bin/fwbcgi in the URI path. Tenable Web App Scanning plugin 115040. Qualys QID pending. FortiWeb access logs show the traversal path in the request URI.Forge cgi_auth() identity via HTTP_CGIINFO header
fwbcgi, the attacker supplies a Base64-encoded JSON blob in the HTTP_CGIINFO header. This JSON contains fields like username, profname, vdom, and loginname, effectively impersonating the built-in admin user. The cgi_auth() function inside fwbcgi parses and trusts this header without verifying it came through the authenticated session manager, granting full admin-level API access to the attacker.- Access to fwbcgi via the path traversal in step 2
- None — this is an inherent design flaw in how fwbcgi validates caller identity
Create rogue administrator account
prof_admin profile, 0.0.0.0/0 trusted-host access, and an attacker-controlled password. The new admin account is persistent across reboots and provides full GUI and CLI access to the FortiWeb appliance. Multiple threat actors have been observed creating accounts with randomized usernames for stealth.- Successful cgi_auth() bypass from step 3
- None once step 3 succeeds — the API accepts the creation request without further challenge
system admin creation events in FortiWeb logs. Config diff monitoring via FortiManager.Weaponize full admin access for lateral movement or data interception
- Rogue admin account from step 4
- Network segmentation behind the WAF may limit lateral movement scope — but the WAF itself is a high-value pivot point
get system admin), remove any unrecognized entries, rotate passwords for all legitimate admins, and rotate any LDAP/RADIUS service account credentials configured on the appliance. Check for modified WAF policies or certificate exports.%3f/../ or %3F/../ followed by cgi-bin/fwbcgi in the URI path. GreyNoise and CrowdSec community blocklists cover known scanner IPs. This provides detection-in-depth but does NOT prevent exploitation if management is exposed — treat as a detection layer, not a blocking control.- Data-plane WAF rules on the FortiWeb itself — the exploit targets the management plane, not traffic flowing through the WAF proxy. A WAF rule on the device cannot protect against attacks on its own management API unless explicitly configured as a reverse proxy for its own management (which is not a supported or recommended configuration).
- FortiWeb trusted-host restrictions on existing admin accounts — the exploit *creates new* admin accounts with
0.0.0.0/0trusted-host scope, bypassing any restrictions on pre-existing accounts. - MFA/FortiToken on existing admin accounts — the path traversal bypasses the authentication layer entirely; MFA is never invoked because the attacker never goes through the login flow.
- SSL VPN or ZTNA in front of other services — these do not protect the FortiWeb management plane unless the management interface itself is placed behind them, which is architecturally unusual.
The supporting signals.
| In-the-Wild Exploitation | Confirmed and widespread. First observed October 6, 2025 by Defused via honeypot captures — over five weeks before formal CVE assignment. GreyNoise documented 8 scanning IPs within 72 hours of public PoC, growing to 8,163 unique source IPs across 1,071 ASNs in 101 countries by January 7, 2026. Initial-access broker k0t actively exploits this CVE alongside CVE-2026-0257 to harvest credentials sold to Qilin and Inc ransomware groups. |
|---|---|
| CISA KEV Status | Listed. Added November 14, 2025. Due date: November 21, 2025 (7-day remediation window — the shortest CISA issues). CISA KEV entry |
| EPSS Score | 0.91965 (≈92nd percentile) — indicating near-certainty of exploitation activity, consistent with observed in-the-wild status |
| Proof-of-Concept Availability | Weaponized and public. watchTowr Labs published full PoC on November 13, 2025. Metasploit module admin/http/fortinet_fortiweb_create_admin (PR #20717 by sfewer-r7) ships turnkey exploitation. GitHub repos: verylazytech/CVE-2025-64446, fevar54/CVE-2025-64446-PoC. Warning: ChocoPoC malware distributed via trojanized PoC repos for this CVE — vet sources carefully. |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8) — Network-reachable, zero complexity, no privileges, no user interaction. All three CIA impacts are High. Scope is Unchanged but that's academic since the target is itself a perimeter trust boundary. |
| Affected Versions | FortiWeb 7.0.0–7.0.11, 7.2.0–7.2.11, 7.4.0–7.4.9, 7.6.0–7.6.4, 8.0.0–8.0.1 |
| Fixed Versions | FortiWeb 7.0.12+, 7.2.12+, 7.4.10+, 7.6.5+, 8.0.2+ — patched versions reject traversal with HTTP 403 |
| Internet Exposure | Censys: ~20,098 FortiWeb instances online. Shodan: ~300–700 after honeypot filtering. FOFA: ~2,700. GreyNoise confirmed scanning across ports 443, 8443, 8080, 9443, 10443. |
| Vendor Advisory | Fortinet PSIRT FG-IR-25-910, published November 14, 2025. Canadian Cyber Centre issued AL25-017. |
| Discovery | Initially spotted by Defused (honeypot operator) via X post on October 6, 2025. Independently confirmed and PoC'd by watchTowr Labs on November 13, 2025. Rapid7 verified and contributed Metasploit module (sfewer-r7). |
Sources.
- CISA KEV Catalog Entry
- Tenable — FortiWeb Zero-Day Path Traversal Analysis
- GreyNoise — FortiWeb CVE-2025-64446 Exploitation Telemetry
- Arctic Wolf — FortiWeb Path Traversal Admin Account Creation
- Rapid7 Metasploit Module PR #20717
- Safeguard — k0t Access Broker Campaign (70K Credentials)
- Canadian Cyber Centre Advisory AL25-017
- CISA FortiWeb Advisory
Why this verdict
- Zero friction in the attack chain: The exploit is unauthenticated, requires no user interaction, has trivial complexity (a single HTTP POST), and targets a management interface that on thousands of deployments is directly internet-facing. Every prerequisite is met by default on an exposed FortiWeb.
- Role multiplier — canonical network edge appliance: FortiWeb is a Web Application Firewall deployed at the network perimeter *by design*. This is not a workstation or internal-only service. 100% of FortiWeb installations occupy the 'network edge appliance' high-value role. Compromise yields: TLS private key exfiltration (fleet-scale traffic decryption), WAF policy disablement (exposing all protected backend applications), LDAP/RADIUS credential harvesting, and direct network pivot into internal infrastructure. Blast radius is tenant-to-fleet scale depending on what sits behind the WAF.
- Confirmed ransomware supply chain: The k0t initial-access broker campaign has weaponized this CVE at scale, harvesting 70,000+ credentials and selling access to Qilin and Inc ransomware affiliates. This isn't theoretical — the exploit-to-ransomware pipeline is documented and operational.
- Exploitation maturity is maximal: Metasploit module ships turnkey, multiple public PoCs circulate, 8,163+ unique IPs scanned for this bug across 101 countries, and exploitation predates disclosure by over five weeks (true zero-day exploitation).
- EPSS 0.92 and KEV-listed with 7-day remediation deadline — both the probabilistic model and CISA's operational judgment agree this is top-tier urgency.
Why not higher?
The CVSS 9.8 is already near-ceiling. The only theoretical path to 10.0 would require Scope: Changed (S:C), but while compromising a WAF has devastating downstream effects, the CVSS definition of scope change requires a formally different security authority — the FortiWeb appliance itself is the authorization scope. The 9.8 with unchanged scope is technically correct; operational impact arguably exceeds it but CVSS has no mechanism to express that.
Why not lower?
Every downgrade lever is absent. There are zero authentication requirements, zero complexity barriers, zero user interaction dependencies. The target is internet-facing by design — this is not a case where 'requires internal network access' provides friction. The product is canonically a high-value network edge component (100% of installs are perimeter security appliances), the floor is CRITICAL. Active exploitation by ransomware-affiliated access brokers with a Metasploit module and 8,000+ scanning IPs eliminates any theoretical argument for latent or unlikely exploitation. Downgrading below CRITICAL would require evidence that FortiWeb is never deployed at the perimeter, which contradicts its entire product purpose.
Crowdsourced verification payload.
Run from any auditor workstation with curl and bash against the target FortiWeb management IP/hostname. No authentication required. Usage: bash check_cve_2025_64446.sh <fortiweb-host> [port] (default port 443). The script first attempts version detection, then falls back to a non-destructive traversal canary (GET, not POST — does not create accounts). Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN.
#!/usr/bin/env bash
# CVE-2025-64446 FortiWeb Vulnerability Check
# Usage: bash check_cve_2025_64446.sh <host> [port]
# Exit: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -uo pipefail
HOST="${1:?Usage: $0 <fortiweb-host> [port]}"
PORT="${2:-443}"
BASE="https://${HOST}:${PORT}"
echo "[*] CVE-2025-64446 check against ${BASE}"
# Step 1: Try to detect FortiWeb version from login page
VERSION=""
LOGIN_RESP=$(curl -sk --max-time 10 "${BASE}/" 2>/dev/null || true)
if echo "$LOGIN_RESP" | grep -qi 'FortiWeb'; then
VERSION=$(echo "$LOGIN_RESP" | grep -oiP '(?:FortiWeb|FWB)[^0-9]*\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
fi
# Step 2: Try status API (some configs leak version)
if [ -z "$VERSION" ]; then
STATUS_RESP=$(curl -sk --max-time 10 "${BASE}/api/v2.0/system/status" 2>/dev/null || true)
VERSION=$(echo "$STATUS_RESP" | grep -oP '"version"\s*:\s*"\K[0-9]+\.[0-9]+\.[0-9]+' | head -1)
fi
# Step 3: Non-destructive traversal canary (GET only — will NOT create accounts)
CANARY_CODE=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 \
-X GET "${BASE}/api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi" \
2>/dev/null || echo "000")
if [ -z "$VERSION" ] && [ "$CANARY_CODE" = "000" ]; then
echo "[?] UNKNOWN - Cannot reach ${BASE} or identify as FortiWeb."
exit 2
fi
echo "[*] Detected version: ${VERSION:-<undetermined>}"
echo "[*] Traversal canary HTTP status: ${CANARY_CODE}"
# Evaluate version if found
if [ -n "$VERSION" ]; then
MAJOR=$(echo "$VERSION" | cut -d. -f1)
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
VULN=0
case "${MAJOR}.${MINOR}" in
7.0) [ "${PATCH:-0}" -le 11 ] 2>/dev/null && VULN=1 ;;
7.2) [ "${PATCH:-0}" -le 11 ] 2>/dev/null && VULN=1 ;;
7.4) [ "${PATCH:-0}" -le 9 ] 2>/dev/null && VULN=1 ;;
7.6) [ "${PATCH:-0}" -le 4 ] 2>/dev/null && VULN=1 ;;
8.0) [ "${PATCH:-0}" -le 1 ] 2>/dev/null && VULN=1 ;;
esac
if [ "$VULN" -eq 1 ]; then
echo "[!] VULNERABLE - FortiWeb ${VERSION} is in the affected range."
echo "[!] Upgrade to 7.0.12+ / 7.2.12+ / 7.4.10+ / 7.6.5+ / 8.0.2+"
exit 1
else
echo "[+] PATCHED - FortiWeb ${VERSION} is outside the affected range."
exit 0
fi
fi
# Fallback: interpret canary response
if [ "$CANARY_CODE" = "403" ]; then
echo "[+] PATCHED - Traversal canary returned 403 Forbidden (patched behavior)."
exit 0
elif [ "$CANARY_CODE" = "200" ] || [ "$CANARY_CODE" = "302" ]; then
echo "[!] VULNERABLE - Traversal canary returned ${CANARY_CODE} (unpatched behavior)."
exit 1
else
echo "[?] UNKNOWN - Canary returned HTTP ${CANARY_CODE}. Manual verification required."
exit 2
fi