This is the master key cabinet on the loading dock, not a hard-to-reach server bug
CVE-2026-35616 is an improper access control flaw in Fortinet FortiClient EMS affecting 7.4.5 through 7.4.6. In practice, it is a pre-auth API access bypass on the EMS management plane: a remote attacker who can reach the HTTPS interface can send crafted requests and obtain privileged API behavior that Fortinet says can lead to unauthorized code or command execution on the EMS server. Fixed paths are 7.4.7+ or the emergency hotfixes 7.4.5.2111.1277073 and 7.4.6.2170.1277073.
The vendor's *critical* call matches reality. Yes, the affected version slice is narrow, and many EMS deployments should be management-only, but the decisive amplifiers swamp that friction: no auth required, active exploitation confirmed, KEV-listed, trivial network reachability when exposed, and the target is a control plane for endpoint policy and software distribution. A compromise here is not just one Windows server; it is a launchpad into every managed endpoint the server can touch.
4 steps from start to impact.
Find an exposed EMS listener
curl/nmap sweep to locate FortiClient EMS on TCP/443. Censys published fingerprints for the product and observed roughly 10,000 exposed EMS hosts, with 3,835 potentially vulnerable by banner.- Target EMS is reachable over HTTPS from the attacker position
- Instance is FortiClient EMS 7.4.5 or 7.4.6 or an unpatched hotfix-equivalent build
- Many mature enterprises keep EMS on internal management networks or behind VPN
- Banner-based exposure counts overestimate true exploitability because some hosts are already hotfixed
Bypass certificate-based API auth
- HTTPS access to the EMS web/API interface
- Target still trusts spoofable
X-SSL-CLIENT-*style headers or equivalent unstripped auth context
- A reverse proxy, WAF, or the Fortinet hotfix that strips spoofable headers breaks this path
- Private-only exposure means the attacker usually needs prior foothold or VPN access
Abuse privileged API endpoints
- Successful pre-auth bypass
- Relevant privileged API routes exposed on the EMS instance
- Blast radius depends on what EMS is actually integrated with: some shops use only a subset of features
- Application logging may capture unusual API use if anyone is watching it
401/auth-related errors.Turn server compromise into fleet impact
- EMS has managed endpoints and/or security integrations enrolled
- Attacker retains API control long enough to stage actions
- Change control, agent enrollment state, or segmentation can limit how many endpoints actually receive attacker actions
- EDR on the EMS host or managed endpoints may catch follow-on payloads even if the auth bypass itself succeeds
The supporting signals.
| In-the-wild status | Yes. Fortinet said exploitation was observed in the wild; CISA ADP marks exploitation as active in the CVE record. |
|---|---|
| KEV status | KEV-listed on 2026-04-06 with a federal due date of 2026-04-09. |
| EPSS | 0.4117 from your intel block; secondary tracking shows this sits roughly in the 97th-98th percentile range after disclosure. |
| PoC / exploit availability | Public exploit logic is effectively available. Bishop Fox published root-cause analysis and a non-destructive scanner; GitHub repo keraattin/CVE-2026-35616 publishes Python/Nmap detection tooling. That is close enough to commoditization for defenders. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — the classic *internet-reachable, no-auth, no-click, full-impact* shape. |
| Affected versions | FortiClient EMS 7.4.5 through 7.4.6. |
| Fixed versions | Primary fix is 7.4.7+. Fortinet also released out-of-band hotfixes 7.4.5.2111.1277073 and 7.4.6.2170.1277073. |
| Exposure data | Censys observed around 10K exposed FortiClient EMS hosts and 3,835 potentially vulnerable by banner at disclosure time. |
| Disclosure timeline | CVE published 2026-04-04; KEV added 2026-04-06; FortiClient EMS 7.4.7 release notes show the CVE fixed in build 2193.M and later 2194.M re-release. |
| Research / technical root cause | Independent reverse engineering from Bishop Fox says the auth bypass comes from trusting spoofable certificate-auth headers at the application layer; their write-up materially increases defender confidence that exploitation is straightforward when the interface is reachable. |
noisgate verdict.
The decisive factor is active exploitation of an unauthenticated network bug on a management plane that directly controls endpoints. The only meaningful downward pressure is exposure population: many EMS servers are not internet-facing, but for the ones that are, this is immediate control-plane compromise with outsized fleet blast radius.
Why this verdict
- KEV + active exploitation erase debate. This is not a hypothetical 9.x that might never be weaponized; CISA and Fortinet both say attackers are already using it.
- Pre-auth network path keeps friction low. No credentials, no user action, and no prior endpoint compromise are required when EMS is reachable over HTTPS.
- Control-plane blast radius is the amplifier. EMS is built to manage, configure, and push changes to large endpoint populations, so compromise of one server can become many downstream impacts.
- Real-world friction exists but is not decisive. Requiring exposure to the EMS interface narrows population, and some enterprises keep it internal or VPN-only, but Censys still saw thousands of potentially vulnerable internet-facing systems.
Why not higher?
There is no higher bucket than CRITICAL here, but the score does not need to be artificially pushed beyond the ceiling. The main moderating factor is that only 7.4.5-7.4.6 are affected, and not every enterprise exposes EMS to the internet. Internal-only deployments meaningfully reduce reachability, though not the danger after initial access.
Why not lower?
Dropping this to HIGH would ignore the two facts that matter most: active exploitation and unauthenticated compromise of a fleet management server. Even with a narrow affected range, the attack path is short and the consequences extend well beyond the single host, which is exactly what the CRITICAL bucket is for.
What to do — in priority order.
- Restrict EMS HTTPS immediately — Put the EMS web/API interface behind VPN, allowlisted admin ranges, or a management jump path immediately, within hours because this CVE is KEV-listed and actively exploited. If the server is internet-reachable today, assume it is in the attacker search space already.
- Strip spoofable auth headers upstream — On any reverse proxy, WAF, or HTTP tier in front of EMS, explicitly drop inbound
X-SSL-CLIENT-*style headers and any equivalent client-cert context headers immediately, within hours. This mirrors the hotfix strategy described by independent analysis and can break the pre-auth path before full remediation lands. - Hunt EMS logs and EDR for post-auth anomalies — Review EMS host telemetry, IIS/Apache-equivalent access logs, and application logs for unusual API calls, new source IPs, auth-bypass-like
401/500sequences, and mass management actions immediately, within hours. On a CRITICAL KEV item, validation of exposure without compromise is not enough; you need an intrusion check. - Segment EMS from broad east-west access — Limit outbound/admin paths from the EMS host to only required infrastructure and enrolled endpoints within 3 days if immediate isolation is impossible. This reduces blast radius if the attacker already obtained control-plane access.
- MFA for administrators does not stop a pre-auth API bypass that sidesteps the normal login path entirely.
- Password rotation alone does not help because the attacker does not need valid credentials to reach privileged functionality.
- Endpoint AV only on managed clients is too late in the chain; the dangerous asset is the EMS control plane, not just the downstream endpoints.
Crowdsourced verification payload.
Run this on the FortiClient EMS server itself in an elevated PowerShell session. Example: powershell -ExecutionPolicy Bypass -File .\check-forticlientems-cve-2026-35616.ps1. Local administrator is recommended so the script can read both 64-bit and Wow6432Node uninstall data reliably.
# check-forticlientems-cve-2026-35616.ps1
# Purpose: Determine likely exposure to CVE-2026-35616 on a Windows FortiClient EMS host
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
$ErrorActionPreference = 'Stop'
function Get-UninstallEntries {
$paths = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$items = foreach ($p in $paths) {
Get-ItemProperty -Path $p -ErrorAction SilentlyContinue |
Where-Object {
($_.DisplayName -match 'FortiClient\s*EMS') -or
($_.Publisher -match 'Fortinet') -and ($_.DisplayName -match 'EMS')
} |
Select-Object DisplayName, DisplayVersion, Publisher, InstallLocation, PSPath
}
$items | Sort-Object DisplayName -Unique
}
function Parse-Version([string]$v) {
if ([string]::IsNullOrWhiteSpace($v)) { return $null }
$clean = ($v -replace '[^0-9\.]','').Trim('.')
if ([string]::IsNullOrWhiteSpace($clean)) { return $null }
$parts = $clean.Split('.') | Where-Object { $_ -ne '' }
$ints = @()
foreach ($p in $parts) {
try { $ints += [int]$p } catch { $ints += 0 }
}
return ,$ints
}
function Compare-VersionArrays($a, $b) {
# returns -1 if a<b, 0 if equal, 1 if a>b
$len = [Math]::Max($a.Count, $b.Count)
for ($i = 0; $i -lt $len; $i++) {
$av = if ($i -lt $a.Count) { [int]$a[$i] } else { 0 }
$bv = if ($i -lt $b.Count) { [int]$b[$i] } else { 0 }
if ($av -lt $bv) { return -1 }
if ($av -gt $bv) { return 1 }
}
return 0
}
function Version-StartsWith($arr, $prefix) {
if ($arr.Count -lt $prefix.Count) { return $false }
for ($i = 0; $i -lt $prefix.Count; $i++) {
if ([int]$arr[$i] -ne [int]$prefix[$i]) { return $false }
}
return $true
}
$entries = Get-UninstallEntries
if (-not $entries -or $entries.Count -eq 0) {
Write-Output 'UNKNOWN: No FortiClient EMS uninstall entry found in standard registry locations.'
exit 2
}
# Prefer the entry with a version value
$entry = $entries | Where-Object { $_.DisplayVersion } | Select-Object -First 1
if (-not $entry) { $entry = $entries | Select-Object -First 1 }
$rawVersion = $entry.DisplayVersion
$ver = Parse-Version $rawVersion
if (-not $ver) {
Write-Output ('UNKNOWN: Found {0} but could not parse DisplayVersion "{1}".' -f $entry.DisplayName, $rawVersion)
exit 2
}
# Known fixed milestones from Fortinet docs:
# 7.4.5.2111.1277073 (GA hotfix 1)
# 7.4.6.2170.1277073 (GA hotfix 1)
# 7.4.7+ (general fixed release)
$prefix745 = @(7,4,5)
$prefix746 = @(7,4,6)
$prefix747 = @(7,4,7)
$fix745 = @(7,4,5,2111)
$fix746 = @(7,4,6,2170)
$status = $null
$reason = $null
if (Version-StartsWith $ver $prefix745) {
if ((Compare-VersionArrays $ver $fix745) -ge 0) {
$status = 'PATCHED'
$reason = '7.4.5 with hotfix build >= 2111 detected.'
} else {
$status = 'VULNERABLE'
$reason = '7.4.5 detected without GA hotfix 1 build threshold.'
}
}
elseif (Version-StartsWith $ver $prefix746) {
if ((Compare-VersionArrays $ver $fix746) -ge 0) {
$status = 'PATCHED'
$reason = '7.4.6 with hotfix build >= 2170 detected.'
} else {
$status = 'VULNERABLE'
$reason = '7.4.6 detected without GA hotfix 1 build threshold.'
}
}
elseif ((Compare-VersionArrays $ver $prefix747) -ge 0) {
$status = 'PATCHED'
$reason = 'Version is 7.4.7 or later.'
}
else {
$status = 'UNKNOWN'
$reason = 'Installed version is outside the directly documented affected range in this check.'
}
Write-Output ('Product : {0}' -f $entry.DisplayName)
Write-Output ('Version : {0}' -f $rawVersion)
Write-Output ('Install Path : {0}' -f $entry.InstallLocation)
Write-Output ('Result : {0}' -f $status)
Write-Output ('Reason : {0}' -f $reason)
switch ($status) {
'PATCHED' { exit 0 }
'VULNERABLE' { exit 1 }
default { exit 2 }
}
If you remember one thing.
Sources
- Fortinet PSIRT advisory FG-IR-26-099
- CVE.org / OpenCVE record for CVE-2026-35616
- NVD entry for CVE-2026-35616
- FortiClient EMS 7.4.5 special notices (hotfix 1)
- FortiClient EMS 7.4.6 special notices (hotfix 1)
- FortiClient EMS 7.4.7 release notes
- Censys advisory and exposure data
- Bishop Fox technical analysis
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.