← Back to Attack Research

SQL injection that ends in a shell: FortiClient EMS and the endpoint-manager blind spot

CVE-2026-21643 is an unauthenticated SQL injection in FortiClient EMS, and it does not stop at the database. Because the data tier runs with high privilege on the management host, a crafted pre-auth request converts a query bug into command execution. Here is the mechanism, why endpoint managers get skipped, and how to prove and re-verify the fix.

The dangerous SQL injection is not the one that reads a table. It is the one on a host where the database service is privileged enough to write to disk and run a command. CVE-2026-21643 is exactly that: an unauthenticated SQL injection in FortiClient EMS, the server that manages an organization's endpoint agents. A crafted HTTP request reaches a handler that builds a SQL statement out of attacker-controlled input, and because the database engine on the EMS host runs with high privilege, the injection does not end at data. It ends at operating-system command execution. NVD scores it CVSS 9.8, and CISA added it to the Known Exploited Vulnerabilities catalog on 13 April 2026. This is CWE-89 doing what CWE-89 does on the wrong host.

Endpoint management servers are a blind spot precisely because they are defensive tools. FortiClient EMS exists to push policy and posture to the endpoint agents across a fleet, so it is trusted, often internet-adjacent for remote-agent check-in, and quietly exempted from the input-validation scrutiny a public web app would get. That is the wrong instinct. A server that manages endpoints is a server that, once compromised, speaks with authority to every endpoint it manages. The class here is old and the fix is well understood; what makes this instance matter is where it lands. Verifiable security.

The mechanism: untrusted string reaches SQL text

Strip the product name away and the bug is a single sentence: attacker-controlled input reaches SQL query text without parameterization. A pre-authentication HTTP request carries a field the server was supposed to treat as data. Instead of binding that field as a parameter, the handler concatenates it directly into the statement it hands the database. The moment the value crosses into query text, the attacker is no longer supplying data; they are supplying syntax. They can close the intended clause, append their own, and have the database execute it.

On many targets that is where the story pauses, at reading or altering rows. FortiClient EMS is not one of those targets. The database service on the EMS host runs with high privilege, and high-privilege database engines expose primitives that reach outside the data tier: stacked queries that run multiple statements in one request, and the ability to write attacker-chosen bytes to a location on disk the operating system will later execute or load. Chain those and the injection stops being a data problem. The database becomes the attacker's hands on the host, and the result is command execution with the privilege of the service. No credentials were presented at any point, because the vulnerable path answers before authentication.

This is not a novel weakness so much as a repeat with a familiar surname. FortiClient EMS carried an earlier SQL injection of the same lineage in CVE-2023-48788, and the fix boundary is identical: parameterize the query so the untrusted value can never be read as SQL syntax. When the same class returns to the same product two years later, the lesson is that any code path where a request field can reach a SQL statement is a candidate until every one binds its inputs.

CVE-2026-21643 ยท DATA-TIER INJECTION TO OS COMMAND pre-auth HTTP request | v [ EMS handler ] --(concatenates field into SQL text)--> [ database ] | | no login required runs with HIGH privilege | stacked query + write-to-disk primitive | v OS command execution on host fix boundary: bind the field as a PARAMETER, never as query text

Affected 7.4.0 through 7.4.4; fixed in 7.4.5. Illustrative, defender-level; no weaponized payload.

Why the endpoint manager is the wrong host to lose

Severity here is not a scanner reflex, it is arithmetic. Unauthenticated means no barrier of entry. SQL injection to command execution means full read, write, and run on the host. And the host is the one that manages your endpoint fleet, which makes it a pivot point rather than a leaf. An attacker who owns the EMS server owns the vantage point that endpoint agents are configured to trust: a bug at the center of the estate, not on its edge.

Where to be precise, because precision is the brand: not every EMS deployment is reachable from an untrusted segment, and only builds 7.4.0 through 7.4.4 are affected, with 7.4.5 carrying the fix. A patched instance, or one whose management surface is confined to a trusted network, carries a small fraction of the risk. The work is not to assume every EMS is exploitable. It is to determine, for your estate, which instances are on an affected build and reachable from somewhere you do not control.

A server that manages endpoints is a server that, once compromised, speaks with authority to every endpoint it manages.

Detection without weaponization

You do not need to run an exploit to know whether you are exposed, and you should not exfiltrate data to prove a point. Two readable facts settle most of it, and a single non-destructive differential settles the rest.

For monitoring, the pre-auth endpoints of an endpoint manager are a small, well-defined surface, which makes anomalous request shapes against them a high-signal indicator. Malformed input aimed at a parameter that should only carry structured data is worth an alert, not a log line.

How Celvex catches this

Find. Prove. Fix. Verify.

Find

A read-only sweep version-fingerprints every EMS instance, flags any reachable from an untrusted segment, and cross-references the build against the KEV entry for CVE-2026-21643, all without sending a malicious payload.

Prove

A boolean or time-based differential on the injectable parameter demonstrates injection without exfiltrating data, and the result becomes an Ed25519-signed Proof Capsule carrying the host, the version evidence, and the KEV entry.

Fix

The capsule names the remediation: upgrade to 7.4.5, which parameterizes the vulnerable query, and restrict the EMS management surface so it no longer answers from untrusted segments.

Verify

Re-run the same differential after patching. No timing or boolean signal, and response-shape invariance across true and false conditions, confirms the query is parameterized and the finding closes.

What we add is not a new CVE. The vulnerability is in the vendor's code, and the upgrade to 7.4.5 is the fix. What we produce is the exposure determination for you: which EMS instances are on an affected build, reachable from where, and therefore carry the catalog's risk right now, backed by a differential that proves the bug and a re-run that proves the patch held.

The endpoint manager gets skipped because it is filed under security tooling, as if the tools that defend the fleet are exempt from the boundary question. They are not. Any place a stranger's input can reach a SQL statement is a place to check, on your management servers most of all. Answer it there and this class stops being a blind spot.

Verifiable security. Find it. Prove it. Fix it. Verify the fix held. That is what we ship.

Sources

Which of your trust anchors take outside input before login?

Free Exposure Check, no signup required. We map your internet-facing management servers, fingerprint their versions against the known-exploited catalog, and ship a signed Proof Capsule for the highest-confidence finding.

Run a Free Scan →