Research details
ComponentDjango cert_chain_auth.py middleware - CertChainAuth.contains_certificate() trusts user-controllable HTTP_X_SSL_CLIENT_VERIFY header and Certificate.validate_cert_chain() performs DN string matching without cryptographic signature verification
PreconditionsThe attacker must have network reachability to the FortiClient EMS web management interface (typically TCP ports 8013 HTTP or 8443/443 HTTPS). No authentication or user interaction is required. The target must be running FortiClient EMS version 7.4.5 or 7.4.6. The attacker sends crafted HTTP requests containing spoofed headers (X-SSL-CLIENT-VERIFY: SUCCESS) and a forged client certificate PEM in the X-SSL-CLIENT-CERT header to bypass Django middleware certificate-based authentication. The middleware trusts these user-controllable HTTP headers as equivalent to Apache mod_ssl WSGI environment variables, and the certificate chain validation performs only Distinguished Name string matching with no cryptographic signature verification, allowing an attacker to forge a passing certificate chain using openssl with the correct DN strings. Successful exploitation grants full administrative API access to all managed endpoints, enabling arbitrary code execution on the EMS server and managed FortiClient endpoints. agent
DeliveryRemote unauthenticated: attacker sends HTTP requests to EMS management interface (port 8013/8443/443) with spoofed X-SSL-CLIENT-VERIFY: SUCCESS header and X-SSL-CLIENT-CERT header containing a URL-encoded self-signed PEM certificate whose subject/issuer DN strings match embedded Fortinet root CAs (e.g., CN='support' or CN='fortinet-ca2'). The cert_chain_auth.py middleware trusts the user-controllable HTTP_X_SSL_CLIENT_VERIFY header over the trusted SSL_CLIENT_VERIFY WSGI variable, and the validate_cert_chain() method performs only DN string comparison with no cryptographic signature verification, granting the attacker certificate-user role access to 16 cert_chain_approved API endpoints spanning endpoint quarantine, EMS configuration, ZTNA key management, and FortiGate device registration.
Public PoCyes
Exploit notesCVSS attack complexity: LOW | CRITICAL severity (CVSS 9.1). Four public PoC repos (Alaatk 37 stars, keraattin, BishopFox, z3r0h3ro). The exploit requires only two HTTP headers (X-SSL-CLIENT-VERIFY: SUCCESS and X-SSL-CLIENT-CERT with URL-encoded forged PEM) and forged certificate DN strings discoverable from any EMS installation, FortiClient agent package, or FortiGate firmware image. No authentication, user interaction, or special privileges required. Confirmed exploited in the wild since March 31, 2026 (Arctic Wolf), delivering EKZ Infostealer to managed endpoints. The hotfix strips headers at the Apache layer via RequestHeader unset directives but leaves the string-only chain validation code in cert_chain_auth.py untouched until version 7.4.7.
SummaryFortiClient EMS 7.4.5 and 7.4.6 contain an authentication bypass (CVE-2026-35616) in the Django cert_chain_auth.py middleware that allows unauthenticated attackers to gain full administrative API access. The middleware accepts certificate verification status from user-controllable HTTP headers (X-SSL-CLIENT-VERIFY) in addition to trusted Apache mod_ssl WSGI environment variables, and Apache was never configured to strip the spoofable variants. The certificate chain validation performs only Distinguished Name string matching against embedded Fortinet root CAs with no X.509 cryptographic signature verification, allowing an attacker to forge a passing certificate chain using openssl with the correct DN strings. Exploitation in the wild was confirmed by Arctic Wolf, delivering EKZ Infostealer disguised as a Fortinet patch to managed endpoints.