CVE-2025-25249 has two official CVSS scores. The National Vulnerability Database says 9.8, critical. Fortinet, the CNA that assigned the CVE, says 8.1, high.

The entire 1.7-point gap comes from one metric. NVD scored attack complexity low. Fortinet scored it high.

By September 2026, SOCRadar's Threat Research Unit had observed more than 30,000 FortiGate IP addresses targeted and 178 confirmed implant sessions. The field settled the disagreement.

[INFO]
Primary sources: SOCRadar Threat Research Unit, CVE-2025-25249 Exploitation Delivers PivotC2, a FortiGate Post-Exploitation RAT(8 September 2026), and the CVE record as published by NVD and by Fortinet as CNA. All indicators below are SOCRadar's.
◈ interactive artifact
Where the 1.7 Points Went
A working CVSS 3.1 base calculator, loaded with this CVE. Flip attack complexity and see the severity band move.

//The Bug

A heap-based buffer overflow in the cw_acd daemon in FortiOS and FortiSwitchManager, reachable on UDP 5246 - CAPWAP Control.

CAPWAP is the protocol a FortiGate uses to manage FortiAP wireless access points. If you run Fortinet wireless, the controller has to listen for access points, and on a lot of deployments it ends up listening on an interface that is more exposed than anyone intended. It is not the management interface, so it does not always get the management interface's restrictions.

Affected, per the CVE record: FortiOS 6.4.0 through 6.4.16, 7.0.0 through 7.0.17, 7.2.0 through 7.2.11, 7.4.0 through 7.4.8 and 7.6.0 through 7.6.3, plus FortiSwitchManager 7.0.0 through 7.0.5 and 7.2.0 through 7.2.6.

The weakness classification also differs by assessor: NVD records CWE-787, out-of-bounds write; Fortinet records CWE-122, heap-based buffer overflow. That one is a difference in granularity rather than substance, but it is a second reminder that "the CVE says" is doing a lot of work in most conversations.

//What Attack Complexity Is Supposed To Mean

AC:H is not "this is hard to write". The CVSS specification is specific: high complexity means successful exploitation depends on conditions beyond the attacker's control. Something must be true of the target that the attacker cannot simply arrange - a race that must be won, a memory layout that must happen to be right, information that must be gathered first.

For a heap overflow, a vendor choosing AC:H is usually saying that reliable exploitation depends on heap state the attacker cannot deterministically control. That is often a reasonable engineering judgement at the time of assignment.

NVD choosing AC:L is saying the opposite: that in practice, an attacker who has done the work can hit this repeatedly without needing luck.

Both are defensible readings of the same bug at the moment of scoring. The difference is that one of them assumes the exploit does not exist yet and the other assumes it does. Once someone writes a reliable exploit, AC:H stops being true, and nothing in the process updates the score.

Why this is not an academic argument

Most organisations drive patch SLAs off CVSS bands. Critical means an emergency window. High means the next maintenance cycle.

9.8 is critical. 8.1 is high. Same vulnerability, same internet-facing daemon, and the two authoritative sources put it on opposite sides of the line that decides whether someone gets paged.

If your process says "use the vendor score", you patched this in a normal cycle. If it says "use NVD", you patched it out of band. Nobody in either organisation did anything wrong.

There is a third, worse case: SOCRadar's own write-up states 9.8 in the body and 7.4 in its FAQ, both attributed to NVD. Secondary reporting inherits and garbles these numbers constantly. The CVE record is the only place worth reading them from, and it is worth reading both vectors there rather than whichever one your scanner surfaced.

And the exploitation data was stale too

CISA recorded an SSVC assessment for this CVE noting exploitation status "none" as of January 2026. By September 2026, 178 confirmed implants.

That is not a criticism of the assessment, which was presumably accurate when made. It is the point: exploitation status is a snapshot, severity scores are assigned once and rarely revisited, and both get consumed months later as though they were current. If a vulnerability decision in your organisation rests on a score or an exploitation flag, the age of that data is part of the decision.

//PivotC2

What lands after exploitation is a custom Node.js RAT, purpose-built for FortiGate appliances.

The chain runs a binary called fortirun.bin, stages JavaScript to /tmp/.i.js, and pulls from a stager endpoint on port 8443 with a path that looks like a per-victim token. Payload obfuscation is XOR with the key pivot, and there is a separate process-injection payload XORed with 0xAB.

A leading-dot filename in /tmp is the oldest hiding trick on Unix and it still works on appliances, because /tmp on an appliance is not somewhere anyone looks and a plain ls will not show it.

What it takes

The collection list is the most useful part of the report, because it tells you what to assume is gone:

/data/config/sys_global.conf.gz - global system configuration
/data/config/global_system_interface.gz - interface configuration
/data/config/sys_vd_root+root.conf.gz - root VDOM configuration
/data/etc/fsv_sync.dat - device secret

A FortiGate configuration is a map of the network: every interface, every VDOM, every policy, every VPN peer, and hashed local credentials. Taking the configuration is the real objective. The implant is how you get it and keep getting it.

fsv_sync.dat matters separately. A device secret is the sort of material that can undermine trust relationships between appliances rather than just one box, and it is not something you rotate by applying a firmware update.

Egress over Tor

Two obfs4proxy Tor bridges appear in the indicator set: 45.138.16[.]182:9130 and 89.217.174[.]207:9001.

obfs4 is a pluggable transport designed to make Tor traffic resist protocol identification by censors. Using it from a compromised firewall means the exfiltration path is deliberately hard to classify, and the destinations that do appear in the data - 46.151.29[.]58 and 146.103.99[.]177 - are bridges and nodes rather than a stable home server.

SOCRadar attributes the campaign to a Russian-speaking, financially motivated operator. Take the attribution at the confidence they state it; the behaviour is consistent with access brokerage, which is the usual commercial purpose for a large harvest of firewall configurations.

//Edge Devices Do Not Give You The Usual Tools

This is the recurring problem with appliance compromise and it is worth stating plainly.

You cannot install an EDR agent on a FortiGate. You often cannot get a shell without vendor involvement. You cannot easily image it. Its logs are what the vendor decided to give you, and an implant with root can edit them. The device sits at the network edge with visibility into everything and is one of the least instrumented things you own.

So the detection options are narrow and mostly external:

Is UDP 5246 reachable from where it should not be? That is a scan you can run yourself, today, without vendor help, and it is the single most valuable action in this article.

Is the firewall making outbound connections it has no reason to make? Watch the device as a source, not just as an enforcement point. Egress from the firewall itself to Tor bridges or unknown hosts is anomalous by definition.

Is there an unexpected Node.js process, or files under /tmp you cannot account for? Requires support engagement on most FortiOS builds, which is a reason to open the case rather than a reason not to look.

And after any suspected compromise, treat the configuration as public. Rotate VPN pre-shared keys, local accounts, RADIUS secrets, anything held on the device. Patching removes the bug. It does not un-steal sys_global.conf.gz.

//What To Take From This

Read both CVSS vectors, not one score. Where NVD and the CNA disagree, the disagreement itself is information about how well the bug was understood at assignment. Feed the higher one into triage until you have a reason not to.

Do not let an exploitation flag substitute for exposure data."No known exploitation" is a statement about the past. Whether the port is reachable from the internet is a statement about you, and you can check it in an afternoon.

Audit what your firewall listens on, not only what it permits. CAPWAP on 5246 is a service most administrators have never thought about, because the wireless controller is configured once and forgotten.

Assume configuration loss on appliance compromise. The credential rotation is the incident response. The patch is housekeeping.

[IOC]
All indicators from SOCRadar STRU, 8 September 2026. Where earlier community reporting circulated different hashes for the same filenames, those are not reproduced here.

fortirun.bin SHA256 2d338ffc8cc80293575c6800c059e33eb41e967907c20ba7687b2231c50837db
payload.js (stager) SHA256 cc7f0660d56405cbdff157033d3e35305f063e62efaff6501d11e6a34e7bd151
payload2.js SHA256 eb4d8aab4e687839c5478a7a3819b0a7a857555ed50fc159c026e99764a0c8a0
payload_new.js SHA256 fe7da807a2b37a2bbd8c27830a9acc0d86ad8128f38489c493873c7e410c0408
run.ps1 SHA256 c25a27b506fbae62010caf2abff699df5c94d29f056fa7ccfb5f3170d917c8cb
payload.b64 SHA256 d4911736986cf8affb29106fb8e8b74e00e52d5f762dce9025f2cfe431cf2140
PivotC2 decoded d99fa14f5e7dfe17e437f167f3f9550ebeda496960710dde81d41748bd7749e4 / encoded 005e6014fb8fd47249691756f5af3b3d53bfae82df88a71277e53e13fe94cb9f (46.151.29.58)
PivotC2 decoded 550f99193f9e90d93b70af1ab050a2d44f1830259ea165568dafc518e761c589 / encoded 08fa6abac9c132deff4f120a7fcfe5bf17c797b87dbbc3f261d5cf0c077c0a2e (146.103.99.177)
PivotC2 decoded a9bea5f89984d47dd60216b0a0b064e8c7e8057e0c10509faa4a2d8d641eb73b / encoded 1bf2c5976f2abbe147ae7be140ed69af2c25092f563786400aecd0231229be19 (146.103.99.177:9443)

Nodes: 46.151.29[.]58, 146.103.99[.]177
Tor bridges (obfs4proxy): 45.138.16[.]182:9130, 89.217.174[.]207:9001
Stager: hxxps://146[.]103[.]99[.]177:8443/0c5b76709523
Implant path: /tmp/.i.js
Harvested: /data/config/sys_global.conf.gz, /data/config/global_system_interface.gz, /data/config/sys_vd_root+root.conf.gz, /data/etc/fsv_sync.dat
XOR keys: pivot (payload), 0xAB (process injection)

//Sourcing and Uncertainty

The vulnerability detail, affected versions, both CVSS vectors and both CWE classifications come from the CVE record as published by NVD and by Fortinet as CNA. The campaign detail, all indicators, the 30,000 targeted and 178 infected figures, the vulnerable component and the attribution come from SOCRadar STRU's 8 September 2026 report. The CISA SSVC exploitation status is from the NVD record.

A correction to earlier reporting, including my own working notes. An earlier pass on this campaign recorded Fortinet's score as 7.4. It is 8.1. The vector - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - was right; the arithmetic on it was not. The calculator above computes from the vector so the number cannot drift again.

Indicators deliberately omitted. A set of secondary IOCs circulated attributing this research to a site that could not be found, including a different hash for fortirun.bin, a longer XOR key and a TLS certificate common name. None of it could be corroborated against SOCRadar or any other source. Publishing an incorrect hash as an IOC is worse than publishing none, because it sends people hunting for a file that does not exist, so those values are not here.

The interpretation is mine: the argument that the AC:L versus AC:H split is a disagreement about whether a reliable exploit exists rather than about the bug, the point about exploitation flags ageing, the reading of the configuration harvest rather than the implant as the objective, and the assessment that credential rotation rather than patching is the actual remediation.