Jun 26, 2026BTA Team9 min read

FortiSandbox's Verdict Engine Hands Out Root: Three Pre-Auth RCEs in the JRPC API Now Being Exploited

There is a particular flavor of bad week for a security team, the one where the appliance that decides whether files are safe quietly stops deciding for itself. FortiSandbox operators are having that week. Three CVEs from the April advisory cycle are now being hit by honeypot sensors and customer telemetry, with the bulk of the traffic routed through the same JRPC API endpoint on TCP 443, as Help Net Security first reported and The Hacker News followed up on with the active exploitation telemetry.

We at BlueTeamAutomation have been pulling on this one for the same reason you probably should be: FortiSandbox is the verdict engine sitting behind the rest of the Fortinet stack. FortiGate, FortiMail, FortiClient, and FortiEDR all defer to it for sandbox judgments before they block, quarantine, or allow. If an attacker can make the grader say "clean" and then get root on the grader itself, the rest of the security tooling around it keeps reporting green while the network is already losing.

Three Bugs, One JRPC Endpoint, Root in a Single Request

The bugs in question are CVE-2026-39813 (path traversal authentication bypass in the JRPC API), CVE-2026-39808 (OS command injection through the JRPC API), and CVE-2026-25089 (OS command injection through the web UI). Fortinet shipped fixes in FortiSandbox 4.4.9 and 5.0.6 back in April. Defused Cyber's honeypot fleet first picked up exploitation attempts on June 15, with POST requests pounding the /jsonrpc/ path over TLS, and Fortinet's PSIRT has now confirmed the in-the-wild activity. Affected installations span FortiSandbox 4.4.0 through 4.4.8 and 5.0.0 through 5.0.5, plus FortiSandbox Cloud and FortiSandbox PaaS 5.0.4 through 5.0.5.

What makes this attractive to an operator with a pile of compromised hosts to clean up is that two of the three bugs each give pre-authentication code execution by themselves. Chain the authentication bypass into the command injection and you have a quiet, scriptable, single-request path from an unauthenticated TCP connection to a root shell on the device that gates your malware decisions.

The /tmp/ Trick That Forges a Logged In Session

CVE-2026-39813 lives in is_valid_session(), the function the JRPC API calls to decide whether an incoming request belongs to a logged in operator. The function takes the session field supplied in the request body and joins it onto the on-disk session directory using os.path.join(DIRRPCSESS, session_id), then checks whether the resulting path exists and whether its mtime is recent enough for the session to be considered alive. There is no validation. The value arrives straight from the request JSON, and os.path.join preserves leading parent-directory components, so when you hand it session: "../../tmp/" it walks the path right back out of the session directory and into the system's real /tmp.

That is the entire bypass. /tmp exists on every Unix box, its mtime ticks every time anything writes to it (which happens dozens of times an hour on a working system), and the freshness check passes. The JRPC API hands you a privileged session without ever asking who you are. From there you can read system version, hostname, serial number, CPU and RAM stats, scan configuration, and a 32 KB encrypted system backup, but more importantly you can now call the rest of the JRPC methods as if you were the operator.

The jid Parameter That Lands a Root Shell

CVE-2026-39808 is the part that turns a forged session into root code execution. The vulnerable surface is the /fortisandbox/job-detail/tracer-behavior endpoint, which takes a jid GET parameter and uses it to look up the trace artifacts for a previously analyzed sample. That parameter goes directly into a system-level shell invocation with no neutralization of metacharacters, so a value like jid=|id| breaks out of the intended command and runs whatever you put between the pipes as the sandbox service user, which is root. Bipin Jitiya's public PoC on GitHub is, in spirit, a one-line curl: a GET against the endpoint with the jid parameter URL-encoded as a piped Unix command.

No multi-stage payload, no memory corruption, no PE side. The class is CWE-78 OS command injection, the kind of bug a first-year security course warns about, and the fact that it survived in a sandboxing appliance is the part that should sting if you ship anything that calls a shell.

The Web UI's Start VNC Sink

CVE-2026-25089 is the third one, and it lives in the web UI rather than the JRPC API. The vulnerable code path is the start VNC feature, where input from an HTTP parameter flows into another shell construction without filtering the usual command separators (semicolons, backticks, $()). Like the JRPC injection, this one needs no authentication, runs with high privilege, and reaches through the management interface, so an attacker who cannot reach the JRPC port still has an option on whatever exposes the web UI.

The pattern across all three bugs is the same: one parameter, one missing sanitizer, one shell. There are no exotic primitives here. The blast radius comes from where the bugs sit, not from how clever they are.

Why a Sandbox Compromise Quietly Breaks Everything Else

This is the heart of why we are writing about it. A compromised FortiSandbox is not just one more box on the network. The integrated Fortinet products treat its verdicts as authoritative input to blocking and quarantine, so an attacker on the appliance can swap verdicts (mark a stager as clean, mark a known good binary as malicious), pivot off the sandbox to read mirrored traffic and detonation results, and use the device's trust relationships to push commands back to FortiGate and FortiClient instances downstream. SecurityWeek's reporting on the exploitation wave underlines why this matters: the appliance is positioned to silently degrade everything else.

Your detection stack also expects FortiSandbox to be a source of truth, not an attacker. The malware that lands after a successful chain rarely has to evade FortiSandbox's behavioral signatures because the analysis engine itself is now under enemy control. Your SOC dashboard goes on saying everything is fine, because the thing reporting in is the thing the attacker owns.

What This Looks Like on the Wire and on the Host

The observable footprint is small but specific. On the JRPC side you are watching for HTTPS POSTs to /jsonrpc/ on TCP 443 whose JSON body carries a session field containing ../ or any non-hex value. Legitimate session values are short hex tokens with no path characters, so a regex on body content is a viable signal even without TLS decryption if you have an ICAP proxy, WAF, or reverse proxy in front of the appliance. On the command-injection side, log requests to /fortisandbox/job-detail/tracer-behavior with shell metacharacters in the jid parameter (|, ;, backticks, $) and anything matching the start VNC web UI endpoint with similar payloads.

Inside the host, the JRPC service runs as root in production. EDR or auditd telemetry from the appliance should show unexpected child processes spawning from the JRPC and web UI service binaries, with parent process names that map to the Fortinet service tree and child commands you do not recognize: id, wget, curl, sh, python, nc. ATT&CK techniques in play are T1190 (Exploit Public-Facing Application) for the initial access, T1059 (Command and Scripting Interpreter) for the injection itself, and T1078.001 (Default Accounts) once the attacker pivots through the appliance's stored credentials to reach other Fortinet devices.

Patch, Restrict, and Replay

Fortinet's fixes are FortiSandbox 4.4.9 and 5.0.6, with Cloud and PaaS instances getting vendor pushed updates on the same schedule. Apply them. If you cannot patch immediately, restrict the JRPC API and the web management interface to a tight allowlist of internal addresses; both attack paths require reachability to those services. Rotate the appliance's local credentials and API tokens once you have patched, since any unauthenticated attacker who hit the box during the exposure window had a clear path to read them.

Then go look. Replay a benign approximation of the path-traversal session string and the jid command-injection payload against your now-patched device in a controlled window, confirm that your IDS, WAF, and EDR signatures fire, and confirm that the appliance refuses both requests. A patched FortiSandbox that does not surface the attempt to your SIEM is still a half fix.

This is the loop BlueTeamAutomation automates end to end through BASzy: continuous BAS replay against the patched fleet, correlation through EDR and SIEM, SOAR-driven response, and compliance evidence captured as the validation runs. The patch is one step, and proving it stuck on the appliance that grades your malware should not be a one off scramble.

Validate your detections against real exploit chains

BASzy continuously simulates current CVEs and campaigns against the EDR and SIEM you already run, local-first.

Explore BASzy →