Back to Blog
OpsecForge EditorialAPI SecuritySources reviewed 2026-10-28

CVE-2026-35616: FortiClient EMS Patch and Response Guide

FortiClient EMS 7.4.5 and 7.4.6 are affected by actively exploited CVE-2026-35616. Check fixed versions, hotfix builds, the CVSS 9.8 vs 9.1 difference, and the CISA KEV entry.

Primary source: authoritative reference

CVE-2026-35616 is a critical improper-access-control vulnerability (CWE-284) in Fortinet FortiClient Endpoint Management Server (EMS). Fortinet says an unauthenticated attacker can execute unauthorized code or commands through crafted requests, and that it has observed exploitation in the wild. The affected server releases are 7.4.5 and 7.4.6. Fortinet's advisory lists EMS 8.0 and 7.2 as not affected.

This issue applies to the EMS management server, not every FortiClient endpoint installation simply because it carries a 7.4.x version number. Inventory the EMS servers you operate and check their complete build and hotfix status.

CVE-2026-35616 at a glance

Every row below was re-checked on October 28, 2026 against Fortinet's advisory (FG-IR-26-099), the CVE record, the NVD API and CISA's KEV feed.

Item Verified information
Affected product FortiClient EMS (the management server)
Affected releases 7.4.5 through 7.4.6
Not affected (per advisory table) FortiClient EMS 8.0 and 7.2
Fixed release Upgrade to 7.4.7 or above (advisory "Solution" column)
Interim fix GA hotfix 1 for 7.4.5 and for 7.4.6, installed with Fortinet's hotfix procedure
Attack prerequisites Network reachable, no privileges, no user interaction (vector AV:N/AC:L/PR:N/UI:N)
Known exploitation Yes. Fortinet marks "Known Exploited: Yes"; CISA added the CVE to the KEV catalog on April 6, 2026
Weakness CWE-284, Improper Access Control

One correction to earlier versions of this guide: they said Fortinet's CVE record also lists 8.0.0 or later as fixed. The CVE record does not list fixed versions at all (it only declares 7.4.5 through 7.4.6 as affected), and the advisory describes 8.0 as "Not affected", which is different from "fixed". The Fortinet advisory also said at publication that 7.4.7 was upcoming; the Fortinet documentation version list now includes 7.4.7 and 7.4.8, but confirm the build you download against Fortinet's own release notes.

Why the score is 9.8 on one page and 9.1 on another

The Fortinet advisory shows both numbers: "CVSSv3 Score 9.8" and "CVSSv3 Temporal Score 9.1". The CVE record published by Fortinet carries a vector with temporal metrics and the score 9.1:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C

NVD lists a Fortinet-supplied (psirt@fortinet.com, type Secondary) metric with only the base part and the score 9.8:

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

The base metrics read as: network attack vector, low complexity, no privileges, no user interaction, unchanged scope, and high impact on confidentiality, integrity and availability. The extra E:F/RL:O/RC:C suffix means Exploit Code Maturity "Functional", Remediation Level "Official Fix" and Report Confidence "Confirmed". Those are temporal metrics: they describe the current state of exploit maturity and fixes, and they can only lower the base score.

You can recompute both numbers from the FIRST CVSS v3.1 specification formulas. This script implements the scope-unchanged path plus the specification's Roundup function:

function roundUp1(x) {
  const i = Math.round(x * 100000);
  return i % 10000 === 0 ? i / 100000 : (Math.floor(i / 10000) + 1) / 10;
}
const W = {
  AV: { N: 0.85, A: 0.62, L: 0.55, P: 0.2 }, AC: { L: 0.77, H: 0.44 },
  UI: { N: 0.85, R: 0.62 }, CIA: { H: 0.56, L: 0.22, N: 0 },
  E: { X: 1, H: 1, F: 0.97, P: 0.94, U: 0.91 },
  RL: { X: 1, U: 1, W: 0.97, T: 0.96, O: 0.95 },
  RC: { X: 1, C: 1, R: 0.96, U: 0.92 },
};
function score(vector) {
  const m = Object.fromEntries(vector.split("/").slice(1).map((p) => p.split(":")));
  const changed = m.S === "C";
  const pr = { N: 0.85, L: changed ? 0.68 : 0.62, H: changed ? 0.5 : 0.27 }[m.PR];
  const iss = 1 - (1 - W.CIA[m.C]) * (1 - W.CIA[m.I]) * (1 - W.CIA[m.A]);
  const impact = changed ? 7.52 * (iss - 0.029) - 3.25 * Math.pow(iss - 0.02, 15) : 6.42 * iss;
  const expl = 8.22 * W.AV[m.AV] * W.AC[m.AC] * pr * W.UI[m.UI];
  let base = 0;
  if (impact > 0) base = changed ? roundUp1(Math.min(1.08 * (impact + expl), 10))
                                 : roundUp1(Math.min(impact + expl, 10));
  const mult = W.E[m.E || "X"] * W.RL[m.RL || "X"] * W.RC[m.RC || "X"];
  return { impact: +impact.toFixed(4), expl: +expl.toFixed(4), base, product: +mult.toFixed(6), temporal: roundUp1(base * mult) };
}

Output for the two vectors (run on Node.js with the full script, which just loops over both strings):

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
{ impact: 5.8731, expl: 3.887, base: 9.8, product: 1, temporal: 9.8 }
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H/E:F/RL:O/RC:C
{ impact: 5.8731, expl: 3.887, base: 9.8, product: 0.9215, temporal: 9.1 }

Both match the published values: the impact sub-score 5.8731 plus the exploitability sub-score 3.887 rounds up to 9.8, and the temporal multiplier 0.97 x 0.95 x 1.0 = 0.9215 turns 9.8 into 9.0307, which rounds up to 9.1. NVD's own record reports an exploitability score of 3.9 and an impact score of 5.9, consistent with these rounded intermediates. So the two pages are not disagreeing about the vulnerability; one reports the base score and the other the base score adjusted by temporal metrics. Both are Critical. The E:F and RL:O values are also a snapshot: they reflect the state when the vendor scored it, and you should not use them to argue the risk is lower than 9.8 suggests for an unpatched server.

What CISA's KEV catalog says

The CISA Known Exploited Vulnerabilities JSON feed (catalog version 2026.10.27) contains this entry. These are the fields as published:

Field Value
vulnerabilityName Fortinet FortiClient EMS Improper Access Control Vulnerability
dateAdded 2026-04-06
dueDate 2026-04-09
requiredAction Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.
knownRansomwareCampaignUse Unknown
cwes CWE-284

The notes field adds: "Please adhere to Fortinet's guidelines to assess exposure and mitigate risks. Check for signs of potential compromise on all internet accessible Fortinet products affected by this vulnerability. Apply any final mitigations provided by the vendor as soon as they become available."

Two practical readings. First, the due date was three days after the addition; that deadline applies to US federal civilian agencies under BOD 22-01, but it shows how CISA rated the urgency. Second, knownRansomwareCampaignUse: Unknown means CISA has no confirmed link, not that there is none, and the field can be updated. NVD's record also carries a CISA Coordinator SSVC entry with exploitation "active", automatable "yes" and technical impact "total".

EPSS: a probability that changes

FIRST's EPSS API (https://api.first.org/data/v1/epss?cve=CVE-2026-35616) returned, for the score date 2026-10-27, an EPSS of 0.93983 (about 94%) at the 0.99845 percentile. EPSS estimates the probability of observed exploitation activity in the next 30 days and is recalculated daily, so the number you see will differ. For a CVE already in KEV it adds little: exploitation is confirmed, so patch status decides priority, not the probability.

What administrators should do now

1. Identify every EMS server and its exact state

Record the EMS release, build, and installed hotfixes for each deployment. Do not infer the server version from managed endpoint agents. Fortinet's release notes (special notices) identify these fixed hotfix builds, each linked to bug ID 1277073:

  • FortiClient EMS 7.4.5 GA hotfix 1: 7.4.5.2111.1277073
  • FortiClient EMS 7.4.6 GA hotfix 1: 7.4.6.2170.1277073

If an affected server cannot be confirmed as patched, treat it as exposed until verified.

A small triage script helps when you have a spreadsheet of servers. The rules below follow the advisory exactly: 7.4.5 through 7.4.6 are affected, the two hotfix builds count as fixed only when emscli execute hotfix --list showed applied, 7.4.7 and above are the upgrade target, and 8.0 and 7.2 are marked not affected. Everything else is routed to a human, because the advisory says nothing about it. The inventory is synthetic.

const parse = (v) => v.split(".").map(Number);
const cmp = (a, b) => {
  const x = parse(a), y = parse(b);
  for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i];
  return 0;
};
const HOTFIX_BUILD = { "7.4.5": "7.4.5.2111.1277073", "7.4.6": "7.4.6.2170.1277073" };

function triage(s) {
  if (cmp(s.release, "7.4.5") >= 0 && cmp(s.release, "7.4.6") <= 0) {
    const want = HOTFIX_BUILD[s.release];
    if (s.hotfixes.some((h) => h.build === want && h.status === "applied")) return "FIXED (GA hotfix 1 applied)";
    return "AFFECTED - patch or confirm";
  }
  if (cmp(s.release, "7.4.7") >= 0 && s.release.startsWith("7.4.")) return "FIXED (7.4.7 or later)";
  if (s.release.startsWith("8.0.") || s.release.startsWith("7.2.")) return "NOT AFFECTED per advisory table";
  return "OUTSIDE ADVISORY RANGE - verify manually";
}

const inventory = [
  { name: "ems-lab-01", release: "7.4.5", hotfixes: [] },
  { name: "ems-lab-02", release: "7.4.6", hotfixes: [{ build: "7.4.6.2170.1277073", status: "applied" }] },
  { name: "ems-lab-03", release: "7.4.6", hotfixes: [{ build: "7.4.6.2170.1277073", status: "pending" }] },
  { name: "ems-lab-04", release: "7.4.7", hotfixes: [] },
  { name: "ems-lab-05", release: "7.2.15", hotfixes: [] },
  { name: "ems-lab-06", release: "7.4.4", hotfixes: [] },
];
for (const s of inventory) console.log(s.name.padEnd(11), s.release.padEnd(7), triage(s));

Output:

ems-lab-01  7.4.5   AFFECTED - patch or confirm
ems-lab-02  7.4.6   FIXED (GA hotfix 1 applied)
ems-lab-03  7.4.6   AFFECTED - patch or confirm
ems-lab-04  7.4.7   FIXED (7.4.7 or later)
ems-lab-05  7.2.15  NOT AFFECTED per advisory table
ems-lab-06  7.4.4   OUTSIDE ADVISORY RANGE - verify manually

This is inventory triage, not a vulnerability scanner. It trusts whatever release and hotfix status you typed in, cannot tell whether a server is reachable, and cannot tell whether it was already compromised before patching. ems-lab-06 shows the intended behavior for gaps: a release the advisory does not mention is flagged for review rather than declared safe. Pending or failed hotfix status (ems-lab-03) stays in the affected bucket.

2. Apply the vendor-supported fix

Prefer upgrading EMS to 7.4.7 or later according to Fortinet's supported upgrade path. If an immediate release upgrade is not possible, obtain the correct EMS hotfix package from the Fortinet Support site and apply it with the documented procedure. Fortinet's own words for the hotfix are that it is "sufficient to prevent it entirely" for the reported issue; treat that as the vendor's statement and still verify the result on your own servers.

Do not substitute a FortiClient endpoint-agent update for the EMS server fix. Back up the deployment, review the release-specific upgrade notes, and plan for the service restart. In Fortinet's published example output, applying the hotfix stops and restarts apache2 and fcems_monitor and replaces one authentication-middleware file, so expect a short management-plane interruption.

3. Reduce exposure while remediation is in progress

Restrict access to the EMS management surface to authorized administration networks and users. Use existing firewall, private-access, or VPN controls rather than exposing the management server broadly. This is a temporary risk reduction measure, not a replacement for the patch. Fortinet states that FortiClient Cloud and FortiSASE were remediated by Fortinet, so customers of those services do not need to take action.

4. Verify the result

After remediation:

  • Confirm the running EMS release and build.
  • If using a hotfix, run Fortinet's documented command (sudo emscli execute hotfix --list on a Linux VM, execute hotfix --list on the EMS CLI) and confirm the latest entry shows Status:applied for your version. Fortinet notes the newest hotfix is always at the top of the list.
  • Verify that EMS services and expected endpoint-management functions recover normally.
  • Preserve the change record, backup reference, and verification evidence for incident review.

How to investigate possible compromise

CISA's KEV listing establishes that exploitation has occurred, and its notes tell operators to check for signs of potential compromise on internet-accessible affected products. Neither the Fortinet advisory nor the CVE record publishes indicators of compromise, log signatures, or a safe public exploit test. Avoid treating an arbitrary URL, status code, or copied community scanner as proof that a server is clean.

Preserve and review the evidence your deployment actually has, including:

  • EMS administrative-account creation and role changes
  • unexpected policy, installer, deployment, connector, or configuration changes
  • authentication and authorization anomalies in EMS and its reverse proxy or access layer
  • unusual management requests or source addresses around the exposure window (Fortinet's advisory was published April 4, 2026; the public records do not say when exploitation began, so start the review window earlier than that date)
  • unexpected server processes, scheduled tasks, files, or outbound connections

These categories are general incident-response guidance, not Fortinet-published indicators. Compare activity against your own administrator and maintenance records. If the evidence suggests unauthorized access, isolate the server using the organization's incident-response process, preserve forensic data, and rotate credentials or tokens that the compromised EMS host could access. Patching stops the known vulnerability; it does not undo prior compromise.

What this advisory does not establish

The public Fortinet and CVE records describe the vulnerability's class, affected versions, impact, and fixes. They do not publicly document the exact vulnerable endpoint, the complete exploit chain, or one authoritative indicator set. This guide therefore does not reproduce speculative request paths, payloads, log signatures, or claims about the attacker's implementation. Fortinet's advisory lists the component as "API" and the impact as "Escalation of privilege", which is a classification label rather than a description of the mechanism.

The vulnerability is an EMS access-control failure. It is not evidence that JWT, OAuth, an API gateway, or a specific application framework caused the defect. General authentication guidance can be useful, but it should not be presented as the confirmed root cause of this CVE.

Share investigation artifacts carefully

Logs and configuration excerpts can contain authorization headers, API keys, passwords, cookies, and internal URLs. Before sharing a small text excerpt with a vendor or teammate, the browser-local Safe-to-Share Sanitizer can help mask common secret shapes. Detection is heuristic: review the result manually and use the organization's approved secure transfer channel for full forensic packages.

For the broader difference between token decoding and security validation, see JWT Vulnerabilities: Validation Failures, HS256 Risks, and Defenses.

Primary sources

Share this: