If your organization runs a Fortinet FortiGate firewall, this is worth reading even if you are confident you patched back in January. A vulnerability that Fortinet fixed eight months ago is now the entry point for an active, ongoing campaign that installs a custom-built piece of malware on compromised firewalls, harvests every credential stored on the device, and uses that access to move deeper into victim networks.
Firewalls sit at the edge of your network by design; they are meant to be the thing that keeps attackers out, not the thing attackers get in through. When a firewall itself is the compromised device, an attacker gains a foothold that is trusted by everything behind it, often invisible to endpoint security tools, and difficult for most organizations to monitor closely. That is exactly the situation now confirmed around CVE-2025-25249.
What is CVE-2025-25249?
CVE-2025-25249 is a heap-based buffer overflow in the cw_acd daemon, a component of FortiOS and FortiSwitchManager responsible for CAPWAP (Control and Provisioning of Wireless Access Points), the protocol Fortinet uses to let a FortiGate firewall centrally manage wireless access points. The daemon listens on UDP port 5246 and processes incoming CAPWAP control traffic.
The flaw allows a remote, unauthenticated attacker to send specially crafted CAPWAP packets that overflow a heap buffer, corrupt adjacent memory, and ultimately execute arbitrary code on the device. No credentials, no user interaction, and no existing session are required; anything that can reach the CAPWAP port can attempt exploitation.
Fortinet's Product Security Team discovered the vulnerability internally and published a coordinated advisory, FG-IR-25-084, on January 13, 2026, alongside patched versions. Because the discovery was internal and the fix shipped at disclosure, there was no early public proof-of-concept and no immediate wave of exploitation, unlike some of the vulnerabilities we have covered in past posts. For most of the first eight months of this vulnerability's life, that made it a routine, if urgent, patch item.
That has changed. Researchers at SOCRadar's Threat Research Unit (STRU) have identified, with high confidence, an active exploitation campaign against CVE-2025-25249 that has been running since at least July 2026 and continues today.
A note on the severity score
You may see this vulnerability described with different severity numbers depending on the source, and that is not an error worth chasing down. Fortinet's own advisory rates it High severity with a CVSS v3.1 base score of 7.3; the U.S. National Vulnerability Database has separately calculated a CVSS v3.1 score of 9.8, Critical, for the same flaw; the Dutch NCSC lists 8.1. This kind of divergence happens when a vendor and a national vulnerability database score the same environmental assumptions differently. Whichever number you track internally, treat this as a high-priority, internet-facing, unauthenticated remote code execution flaw in core firewall infrastructure.
Who is affected?
| Product | Affected Versions | Fixed In |
|---|---|---|
| FortiOS | 6.4.0 through 6.4.16 | 6.4.17+ |
| FortiOS | 7.0.0 through 7.0.17 | 7.0.18+ |
| FortiOS | 7.2.0 through 7.2.11 | 7.2.12+ |
| FortiOS | 7.4.0 through 7.4.8 | 7.4.9+ |
| FortiOS | 7.6.0 through 7.6.3 | 7.6.4+ |
| FortiSwitchManager | 7.0.0 through 7.0.5 | 7.0.6+ |
| FortiSwitchManager | 7.2.0 through 7.2.6 | 7.2.7+ |
| FortiSASE | 25.1.a.2 | Migrate to a fixed release |
| FortiSASE | 25.2.b | 25.2.c+ |
Consult the official Fortinet advisory for the complete and current version matrix before making patching decisions.
The active exploitation campaign
STRU's analysis found that the threat actor behind this campaign built a purpose-made exploitation toolchain rather than relying on a public, off-the-shelf exploit. That is a meaningful distinction from several vulnerabilities we have covered previously, where a leaked or published proof-of-concept let opportunistic, low-skill attackers pile on within days. Here, the barrier to entry was higher, which likely explains why exploitation activity built quietly over months rather than exploding immediately after disclosure.
The exploit chain
The core exploit tool, referred to in the researchers' analysis as fortirun.bin, is a self-extracting Linux binary that bundles a full Python runtime and two purpose-built modules. The exploitation sequence works roughly as follows:
- Fingerprinting and ASLR bypass. The tool sends CAPWAP Discovery Request packets to the target and reads the Access Controller's response, which leaks two identifiers: the hardware revision and the firmware build string. Critically, that same discovery response also leaks live memory addresses for the daemon's code and data sections. Address Space Layout Randomization is supposed to make those addresses unpredictable on each boot; because the daemon hands them to any unauthenticated caller who asks, ASLR provides no real protection here. The tool matches the fingerprint against a hardcoded table covering thirteen FortiGate models and two FortiAP models running specific FortiOS 7.4 builds, and loads the correct memory offsets for that exact build.
- Heap grooming. The exploit sends a series of crafted CAPWAP "Add Station" messages, the kind normally generated when a wireless client joins an access point, to force the daemon's memory allocator into a predictable layout. This positions an attacker-controlled buffer directly adjacent to an internal memory pool structure the exploit intends to corrupt.
- The overflow itself. A single crafted CAPWAP "Image Data" message, a message type ordinarily used for firmware updates, misstates its own size. The daemon copies more data than it allocated space for, overwriting pointer fields in the adjacent memory pool structure with attacker-chosen values.
- Arbitrary write and control-flow hijack. The corrupted pointers grant a write-what-where primitive during the pool's internal cleanup logic, which the exploit uses to overwrite a function pointer with the address of a small, already-present instruction sequence in the daemon's own code. That technique, known as return-oriented programming, lets the attacker redirect execution without needing to inject any new code, sidestepping the operating system's write-versus-execute protections.
- Reverse shell. The hijacked execution path launches a Node.js reverse shell back to the attacker. Fortinet ships Node.js as part of the FortiOS management plane, so the attacker is reusing software that is already present on the device rather than dropping new tools onto it.
From there, a second module delivers a single-line JavaScript stager into the live Node.js session. That stager quietly downloads, decodes, and decrypts a second-stage payload, writes it to a temporary file, and launches it as a detached background process that survives even if the initial connection is closed.
PivotC2: the payload
That second stage is PivotC2, a feature-rich, purpose-built remote access trojan written in Node.js and designed specifically for post-exploitation on FortiGate appliances. Researchers assess, based on unusually detailed inline code comments and structured usage guidance in the recovered source, that the malware's authors made heavy use of AI coding assistance during development. The recovered version, 0.2.3, suggests the tool is still under active development.
PivotC2 gives an operator:
- Interactive access. Shells spawned via
/bin/sh,bash,ash,dash, orbusybox, with output streamed back over the malware's own channel protocol. - File operations. Upload, download, list, read, move, and delete files on the compromised device.
- Tunneling. Built-in SOCKS5 and HTTP proxying, plus local and remote port forwarding, letting the attacker reach hosts on the internal network that are not otherwise exposed to the internet.
- Discovery. CIDR-range port scanning, interface enumeration, and DNS lookups.
- Credential theft. Automated harvesting of FortiGate configuration files, followed by decryption of the encrypted (
ENC) fields inside them using two different cryptographic schemes depending on how each field was originally encrypted. Successfully decrypted material includes VPN pre-shared keys, SSL-VPN user credentials, wireless network passwords, LDAP bind credentials, and administrator accounts, all of which are reusable elsewhere in the victim's environment. - Cleanup. A
killcommand that wipes infection artifacts and terminates the process on demand.
PivotC2 always initiates its own outbound connection to the attacker's server over TLS, which lets it bypass inbound firewall rules entirely; the compromised firewall reaches out to the attacker rather than the other way around.
An "auto-mode" flag turns this into a largely hands-off operation. On every new infection, the C2 server automatically runs a five-stage pipeline: register the victim, harvest and decrypt its configuration, extract the internal network ranges from that configuration, and launch background port scans against both the newly discovered subnets and a set of common private IP ranges. An attacker running in auto-mode does not need to touch a keyboard for a device to be profiled, have its credentials stolen, and have its internal network mapped.
Scale and impact
STRU's analysis of the attacker's own files found a target list of more than 30,000 FortiGate IP addresses, of which 178 were confirmed exploited and infected with PivotC2. The heaviest concentration of infections was in the United States, followed by Chile, Colombia, and the United Kingdom.
Two of the US-based intrusions went well beyond the firewall itself. Investigators found evidence of internal SSH tunneling, Active Directory enumeration, browser credential theft from Chrome and Edge, registry changes enabling Remote Desktop Protocol with pass-the-hash-friendly settings, and exfiltration of Microsoft Exchange mailbox archives to attacker-controlled cloud storage. Russian-language comments in the recovered tooling, combined with the exfiltration tradecraft and a focus on backup and storage infrastructure, led researchers to assess with high confidence that this is a Russian-speaking, financially motivated cybercrime operation, and one that appears to also be probing other known vulnerabilities in FortiManager, PeopleSoft, and ArubaOS.
This is a separate campaign from the FortiBleed operation we have discussed before; there is no infrastructure, tooling, or victim overlap between the two. What they share is a playbook that has clearly proven effective against Fortinet appliances: get in through an edge device, extract and decrypt every stored credential you can find, and use those credentials to move inland.
Detection
Because PivotC2 runs inside a Node.js runtime that legitimately ships with FortiOS, process names alone are not a reliable indicator; a running node process on a FortiGate is not automatically suspicious. Look instead for specific artifacts and connections.
From the FortiOS CLI, check for active sessions to known command-and-control infrastructure:
diagnose sys session filter daddr 146.103.99.177
diagnose sys session filter daddr 46.151.29.58
diagnose sys session list
Check for the staged payload file:
fnsysctl ls -la /tmp/.i.js
fnsysctl ls -la /tmp/
Check for unauthorized Node.js activity beyond what your normal management plane usage would produce:
diagnose sys process list | grep node
Additional known indicators, including file hashes for the exploit binary and PivotC2 client, are documented in SOCRadar's technical writeup.
What should you do?
1. Check your version and patch immediately
Confirm your running FortiOS, FortiSwitchManager, or FortiSASE version against the table above, and upgrade to a fixed release regardless of whether you believe you already applied the January patch cycle:
# Check current FortiOS version
get system status
Target versions: FortiOS 7.6.4, 7.4.9, 7.2.12, 7.0.18, or 6.4.17, whichever is appropriate for your branch; FortiSwitchManager 7.2.7 or 7.0.6; and FortiSASE 25.2.c or a current migrated release for 25.1.a.
2. Reduce exposure while you confirm your patch status
If you cannot verify your patch level immediately, apply interim mitigations:
- Restrict external access by disabling the
fabricservice on internet-facing interfaces where it is not required. - Apply a local-in policy to drop inbound UDP traffic on CAPWAP Control ports (5246 through 5249) from untrusted networks.
- Restrict administrative and management interfaces to trusted IP ranges only.
3. Hunt for existing compromise before you consider this closed
Patching stops new exploitation attempts; it does not remove an implant that is already running or undo credential theft that has already happened. Run the detection checks above against every exposed FortiGate appliance in your environment, not just ones you suspect.
4. Rotate credentials if you find anything
If you find PivotC2 artifacts, a suspicious session to a known command-and-control address, or anything else that suggests compromise, treat the entire device configuration as exfiltrated. That means rotating administrative passwords, SSL-VPN user credentials, LDAP bind secrets, wireless pre-shared keys, and IPsec pre-shared keys defined anywhere in that configuration, and doing so promptly rather than waiting for a maintenance window.
5. Assume the firewall was a pivot point, not the destination
Given that confirmed intrusions in this campaign moved well past the firewall itself, any confirmed compromise should trigger a broader review of internal hosts for the follow-on activity researchers observed: unexpected RDP registry changes, reverse SSH tunnels, browser credential access, Active Directory enumeration tooling, and outbound transfers to cloud storage providers your organization does not normally use.
A note on timing
Nothing about the underlying vulnerability changed between January and July of this year. What changed is that a capable, patient threat actor built a working exploit chain, developed a custom RAT with what appears to be AI assistance, and spent months quietly working through a target list of tens of thousands of internet-facing firewalls before anyone outside the operation noticed. That is a useful reminder that a vulnerability being old and already patched by the vendor says nothing about whether it is still exploitable in your specific environment; it only tells you whether the fix exists, not whether it has been applied everywhere it needs to be.
Summary
| CVE | CVE-2025-25249 |
| Malware delivered | PivotC2 (custom Node.js RAT) |
| CVSS Score | 7.3 (High, Fortinet) / 9.8 (Critical, NVD v3.1) / 8.1 (NCSC) |
| Type | Heap-based buffer overflow (CWE-122, CWE-787) |
| Component | cw_acd daemon (CAPWAP), UDP port 5246 |
| Discovered by | Fortinet Product Security Team (internal) |
| Disclosed | January 13, 2026 |
| Affected | FortiOS 6.4 through 7.6.3; FortiSwitchManager 7.0.x and 7.2.x; select FortiSASE builds |
| Active exploitation? | Yes, confirmed by SOCRadar STRU, ongoing since at least July 2026 |
| Public PoC? | No known public PoC; attackers used a custom-built exploit toolchain |
| Scale | Over 30,000 targeted IPs; 178 confirmed PivotC2 infections; two confirmed full network intrusions |
| Attribution | Assessed with high confidence to be a Russian-speaking, financially motivated cybercrime operator |
| Immediate mitigation | Restrict CAPWAP Control ports (UDP 5246-5249) from untrusted networks; disable external fabric service where unneeded |
| Full fix | Upgrade to FortiOS 7.6.4, 7.4.9, 7.2.12, 7.0.18, or 6.4.17; FortiSwitchManager 7.2.7 or 7.0.6; FortiSASE 25.2.c or later |
If you need help confirming your Fortinet devices are patched, hunting for signs of PivotC2 in your environment, or managing credential rotation on appliances that may have been exposed, get in touch. A patched vulnerability is not the same thing as a closed one, and this campaign is a clear example of why that gap matters.