# [HIGH] Critical Fortinet FortiSandbox flaws now exploited in attacks

**Source:** BleepingComputer
**Published:** 2026-06-16
**Article:** https://www.bleepingcomputer.com/news/security/critical-fortinet-fortisandbox-flaws-now-exploited-in-attacks/

## Threat Profile

Critical Fortinet FortiSandbox flaws now exploited in attacks 
By Sergiu Gatlan 
June 16, 2026
05:19 AM
0 
Attackers are now exploiting several critical vulnerabilities in Fortinet's FortiSandbox cyber threat detection platform, according to threat intelligence company Defused.
Fortinet released security updates for these three critical-severity security flaws (tracked as CVE-2026-39813 , CVE-2026-39808 , and CVE-2026-25089 ) on April 14.
These flaws allow unauthenticated threat actors to escala…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-39813`
- **CVE:** `CVE-2026-39808`
- **CVE:** `CVE-2026-25089`
- **CVE:** `CVE-2025-61624`
- **CVE:** `CVE-2026-26083`
- **CVE:** `CVE-2026-21643`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1486** — Data Encrypted for Impact
- **T1003.001** — LSASS Memory
- **T1003** — OS Credential Dumping
- **T1021.002** — SMB/Windows Admin Shares
- **T1569.002** — Service Execution
- **T1059.004** — Command and Scripting Interpreter: Unix Shell
- **T1078** — Valid Accounts (auth bypass)
- **T1083** — File and Directory Discovery
- **T1068** — Exploitation for Privilege Escalation
- **T1574** — Hijack Execution Flow
- **T1071.001** — Application Layer Protocol: Web Protocols
- **T1041** — Exfiltration Over C2 Channel
- **T1568** — Dynamic Resolution

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### Inbound exploit POST to FortiSandbox tracer-behavior endpoint (CVE-2026-39808)

`UC_44_4` · phase: **delivery** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime values(Web.http_user_agent) as user_agent values(Web.status) as status from datamodel=Web where Web.url="*/fortisandbox/job-detail/tracer-behavior*" Web.http_method="POST" by Web.src Web.dest Web.url _time 
| `drop_dm_object_name(Web)`
| eval shell_meta=if(match(url, "(?i)(%3[Bb]|%26|%7[Cc]|%60|%24%28|%3E|;|\\||&|`|\\$\\(|\\$\\{)"), 1, 0)
| where shell_meta=1 OR isnotnull(url)
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| table firstTime lastTime src dest url user_agent status count
```

**Defender KQL:**
```kql
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has "/fortisandbox/job-detail/tracer-behavior"
   or AdditionalFields has "/fortisandbox/job-detail/tracer-behavior"
| extend ShellMeta = extract(@"(?i)(%3[Bb]|%26|%7[Cc]|%60|%24%28|;|\||&|`|\$\()", 0, RemoteUrl)
| project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine,
          RemoteIP, RemotePort, RemoteUrl, ShellMeta
| order by Timestamp desc
```

### Unauthenticated JRPC API access to FortiSandbox (CVE-2026-39813)

`UC_44_5` · phase: **delivery** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count values(Web.http_user_agent) as user_agents values(Web.status) as statuses values(Web.http_method) as methods dc(Web.src) as src_count from datamodel=Web where (Web.url="*/jrpc*" OR Web.url="*/api/jrpc*" OR Web.url="*/jsonrpc*") by Web.dest Web.src Web.url _time span=5m
| `drop_dm_object_name(Web)`
| where like(dest, "%fortisandbox%") OR like(dest, "%fsa-%")
| where match(src, "^(?!10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|fortigate)")
| stats sum(count) as total_requests values(url) as urls values(methods) as methods values(statuses) as statuses by src dest user_agents
| where total_requests > 1
```

**Defender KQL:**
```kql
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has_any ("/jrpc", "/api/jrpc", "/jsonrpc")
| where RemoteUrl has_any ("fortisandbox", "fsa")
   or AdditionalFields has_any ("fortisandbox", "fsa")
| summarize RequestCount = count(),
            FirstSeen = min(Timestamp),
            LastSeen = max(Timestamp),
            URLs = make_set(RemoteUrl, 10)
            by DeviceName, InitiatingProcessFileName, RemoteIP
| where RequestCount > 1
| order by FirstSeen desc
```

### Path traversal patterns in URIs to FortiSandbox (CVE-2025-61624 chain)

`UC_44_6` · phase: **exploit** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count values(Web.http_method) as methods values(Web.http_user_agent) as user_agents values(Web.status) as statuses from datamodel=Web where (Web.url="*../*" OR Web.url="*..%2f*" OR Web.url="*..%2F*" OR Web.url="*%2e%2e%2f*" OR Web.url="*%2e%2e/*" OR Web.url="*..%5c*") by Web.src Web.dest Web.url _time
| `drop_dm_object_name(Web)`
| where like(dest, "%fortisandbox%") OR like(dest, "%fsa-%")
| eval depth=mvcount(split(url,".."))-1
| where depth >= 2
| table _time src dest url methods user_agents statuses count depth
```

**Defender KQL:**
```kql
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl matches regex @"(?i)(\.\./|\.\.%2[fF]|%2[eE]%2[eE]%2[fF]|%2[eE]%2[eE]/|\.\.%5[cC])"
| where RemoteUrl has_any ("fortisandbox", "fsa-", "job-detail", "/api/")
   or AdditionalFields has_any ("fortisandbox", "fsa-")
| extend TraversalDepth = countof(RemoteUrl, "..")
| where TraversalDepth >= 2
| project Timestamp, DeviceName, InitiatingProcessFileName, RemoteIP, RemotePort, RemoteUrl, TraversalDepth
| order by Timestamp desc
```

### Anomalous outbound C2 from FortiSandbox appliance to non-Fortinet infrastructure

`UC_44_7` · phase: **c2** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=true count sum(All_Traffic.bytes_out) as bytes_out dc(All_Traffic.dest_ip) as dest_ip_count values(All_Traffic.dest_port) as dest_ports values(All_Traffic.dest) as destinations from datamodel=Network_Traffic where All_Traffic.src_category="fortisandbox" OR All_Traffic.src IN ("FORTISANDBOX_IP_1","FORTISANDBOX_IP_2") by All_Traffic.src _time span=10m
| `drop_dm_object_name(All_Traffic)`
| search NOT destinations IN ("*.fortinet.com","*.fortiguard.com","*.fortinet.net","*.fortinet-us.com","*.fortiguard.net")
| search NOT destinations IN ("pool.ntp.org","*.pool.ntp.org")
| where bytes_out > 100000 OR dest_ip_count > 5
| eval suspicious=case(bytes_out > 1000000, "HighVolumeEgress", dest_ip_count > 10, "FanoutBeaconing", 1=1, "Anomalous")
| table _time src destinations dest_ports bytes_out dest_ip_count suspicious count
```

**Defender KQL:**
```kql
let FortiSandboxIPs = dynamic(["10.0.0.0"]);  // <- replace with appliance inventory
let FortinetDomains = dynamic(["fortinet.com", "fortiguard.com", "fortinet.net", "forticloud.com"]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where LocalIP in (FortiSandboxIPs)
| where RemoteIPType == "Public"
| where not(RemoteUrl has_any (FortinetDomains))
| summarize ConnCount = count(),
            BytesEstimate = countif(isnotempty(RemoteIP)) * 1500,
            DestIPs = make_set(RemoteIP, 20),
            DestUrls = make_set(RemoteUrl, 20),
            Ports = make_set(RemotePort, 10),
            FirstSeen = min(Timestamp),
            LastSeen = max(Timestamp)
            by LocalIP, bin(Timestamp, 1h)
| where ConnCount > 10 or array_length(DestIPs) > 5
| order by FirstSeen desc
```

### Ransomware-style mass file rename / extension change

`UC_RANSOM_ENCRYPT` · phase: **actions** · confidence: **Medium**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count, dc(Filesystem.file_name) AS files
    from datamodel=Endpoint.Filesystem
    where Filesystem.action IN ("modified","renamed")
    by Filesystem.dest, Filesystem.user, _time span=1m
| `drop_dm_object_name(Filesystem)`
| where files > 200
| sort - files
```

**Defender KQL:**
```kql
DeviceFileEvents
| where Timestamp > ago(1d)
| where InitiatingProcessAccountName !endswith "$"
| where ActionType in ("FileRenamed","FileModified")
| summarize files = dcount(FileName) by DeviceName, InitiatingProcessAccountName, bin(Timestamp, 1m)
| where files > 200    // empirical: > 200 unique-file renames in 1m by one account on one host
                       //            is well above the P99 of legitimate bulk-tooling
| order by files desc
```

### LSASS process access / dump (credential theft)

`UC_LSASS` · phase: **actions** · confidence: **High**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
    from datamodel=Endpoint.Processes
    where (Processes.process="*lsass*" OR Processes.process="*sekurlsa*"
        OR Processes.process="*MiniDump*" OR Processes.process="*comsvcs.dll*MiniDump*"
        OR Processes.process="*procdump*lsass*")
       OR (Processes.process_name="rundll32.exe" AND Processes.process="*comsvcs*MiniDump*")
    by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
```

**Defender KQL:**
```kql
DeviceEvents
| where Timestamp > ago(7d)
| where AccountName !endswith "$"
| where ActionType == "OpenProcessApiCall"
| where FileName =~ "lsass.exe"
| where InitiatingProcessFileName !in~ ("MsSense.exe","MsMpEng.exe","csrss.exe",
                                          "svchost.exe","wininit.exe","services.exe",
                                          "lsm.exe","SearchProtocolHost.exe")
| project Timestamp, DeviceName, ActionType, FileName,
          InitiatingProcessFileName, InitiatingProcessCommandLine,
          InitiatingProcessFolderPath, AccountName
| order by Timestamp desc
```

### Remote service execution — PsExec / SMB lateral movement

`UC_LATERAL_PSEXEC` · phase: **actions** · confidence: **High**

**Splunk SPL (CIM):**
```spl
| tstats `summariesonly` count min(_time) as firstTime max(_time) as lastTime
    from datamodel=Endpoint.Processes
    where Processes.process_name IN ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
       OR (Processes.process_name="wmic.exe" AND Processes.process="*/node:*")
    by Processes.dest, Processes.user, Processes.process_name, Processes.process, Processes.parent_process_name
| `drop_dm_object_name(Processes)`
```

**Defender KQL:**
```kql
DeviceProcessEvents
| where Timestamp > ago(7d)
| where AccountName !endswith "$"
| where FileName in~ ("psexec.exe","psexesvc.exe","paexec.exe","smbexec.py")
   or (FileName =~ "wmic.exe" and ProcessCommandLine has "/node:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| order by Timestamp desc
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-39813`, `CVE-2026-39808`, `CVE-2026-25089`, `CVE-2025-61624`, `CVE-2026-26083`, `CVE-2026-21643`


## Why this matters

Severity classified as **HIGH** based on: CVE present, 8 use case(s) fired, 14 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
