Safely detect whether a FortiClient EMS server is vulnerable to CVE-2026-35616 without causing any disruption. See the full write-up on the Bishop Fox blog.
CVE-2026-35616 is a pre-authentication API bypass in FortiClient EMS 7.4.5 and 7.4.6 that allows remote, unauthenticated attackers to bypass certificate-based authentication through HTTP header spoofing. The Django application trusts user-controllable HTTP headers (X-SSL-CLIENT-VERIFY, X-SSL-CLIENT-CERT) as equivalent to Apache mod_ssl WSGI environment variables, and the Apache configuration never strips them. Combined with certificate chain validation that performs only Distinguished Name string matching (no cryptographic signature verification), an attacker can forge certificates and gain authenticated API access. Fortinet has confirmed exploitation in the wild.
This tool performs non-destructive vulnerability testing by:
- Sending a baseline POST to
/api/v1/fabric_device_auth/fortigate/initwith no spoofed headers - Sending the same POST with
X-SSL-CLIENT-VERIFY: SUCCESSbut no certificate data - Comparing the two responses to determine if the header reached Django
Sending the verify header with no certificate PEM data triggers the detection path but cannot authenticate because no certificate chain is provided. The server remains stable and can continue handling connections.
- Vulnerable servers will return different responses: the baseline returns HTTP 401 ("Certificate not found in request header") while the spoofed request returns HTTP 500 (server error), because
contains_certificate()passes the gate butvalidate_cert_chain()crashes on missing PEM data. - Patched servers will return identical HTTP 401 responses for both requests, because the hotfix adds Apache
RequestHeader unsetdirectives that strip the spoofed header before it reaches Django. - Non-EMS servers will fail to reach the endpoint, in which case the tool reports an
INCONCLUSIVEresult.
git clone https://github.com/BishopFox/CVE-2026-35616-check
cd CVE-2026-35616-checkNo external dependencies. The scanner uses only Python standard library modules.
Test a FortiClient EMS server at <TARGET>:<PORT>. The default port is 443/TCP.
python3 CVE-2026-35616-check.py <TARGET> [PORT]$ python3 CVE-2026-35616-check.py 192.168.1.1
======================================================================
FortiClient EMS CVE-2026-35616 Vulnerability Scanner
Non-Destructive Detection
======================================================================
[*] Target: 192.168.1.1:443
[*] Testing for CVE-2026-35616 (non-destructive)
[1] Sending baseline POST (no spoof headers)... HTTP 401
[2] Sending POST with X-SSL-CLIENT-VERIFY: SUCCESS... HTTP 500
[*] Analyzing responses...
[+] Baseline: 401 — Certificate not found in request header.
[+] Spoofed: 500 — Server encountered an error, please try again later.
[!] Spoofed header changed server behavior
[!] X-SSL-CLIENT-VERIFY is reaching Django (hotfix not applied)
======================================================================
RESULT: VULNERABLE to CVE-2026-35616
Affected versions: FortiClient EMS 7.4.5 - 7.4.6
Recommendation: Apply Fortinet hotfix or upgrade to 7.4.7+
======================================================================$ python3 CVE-2026-35616-check.py 192.168.1.1
======================================================================
FortiClient EMS CVE-2026-35616 Vulnerability Scanner
Non-Destructive Detection
======================================================================
[*] Target: 192.168.1.1:443
[*] Testing for CVE-2026-35616 (non-destructive)
[1] Sending baseline POST (no spoof headers)... HTTP 401
[2] Sending POST with X-SSL-CLIENT-VERIFY: SUCCESS... HTTP 401
[*] Analyzing responses...
[+] Baseline: 401 — Certificate not found in request header.
[+] Spoofed: 401 — Certificate not found in request header.
[-] Responses identical (header stripped by Apache)
======================================================================
RESULT: NOT VULNERABLE (hotfix applied)
Apache is stripping X-SSL-CLIENT-VERIFY before it reaches Django
======================================================================The tool can only detect the vulnerability if the following conditions are met:
- The target is running FortiClient EMS 7.4.5 or 7.4.6
- The EMS web interface is accessible
- The
/api/v1/fabric_device_auth/fortigate/initendpoint exists and usescert_chain_approvedauthentication
If any condition isn't met, the tool will report an INCONCLUSIVE result.
This code is distributed under an MIT license.
Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.