Get Your Free Report
Start for Free
SOCRadar® Cyber Intelligence Inc. | CVE-2025-25249 Exploitation Delivers PivotC2, a FortiGate Post-Exploitation RAT
Sep 08, 2026
31 Mins Read
Moon
Summarize with:

CVE-2025-25249 Exploitation Delivers PivotC2, a FortiGate Post-Exploitation RAT

One of the most common entry points for attackers is the exploitation of public-facing edge devices (such as VPNs, routers, and firewalls). Over the years, FortiGate firewalls have remained a consistent target, as evidenced by the recent widespread FortiBleed campaign conducted jointly by the INC and Lynx ransomware groups.

The SOCRadar Threat Research Unit (STRU) identified, with high confidence, exploitation of CVE-2025-25249, a heap-based buffer overflow vulnerability in FortiOS and FortiSwitchManager cw_acd daemon. Successful exploitation delivers PivotC2, a Node.js Remote Access Trojan (RAT) designed specifically as a FortiGate post-exploitation tool. PivotC2 supports features such as interactive shells, tunneling, network scanning, and configuration harvesting.

Based on the observed inline comments and usage guidance, the actors highly likely leveraged AI to develop the RAT. Active exploitation has been observed since at least July 2026 and is still ongoing. The threat actors targeted more than 30,000 IP addresses, leading to the exploitation and infection of 178 devices with PivotC2. The highest concentration was in the United States, where two full-intrusion compromises resulted in confirmed data exfiltration. Based on the observed tradecraft, STRU assesses with high confidence that this is a Russian-speaking cybercrime-driven campaign.

Key Points

  • STRU identified with high confidence active exploitation of CVE-2025-25249, a heap-based buffer overflow vulnerability within the FortiOS and FortiSwitchManager cw_acd daemon.
  • The attack chain begins with an exploit binary targeting a vulnerable FortiGate instance to establish a reverse shell and execute a single-line JavaScript command via Node.js.
  • The final payload, PivotC2, is an AI-assisted Node.js RAT tailored for post-exploitation on compromised FortiGate firewalls.
  • PivotC2 establishes a persistent outbound TLS connection to a remote command-and-control (C2) server. Its feature set includes interactive shells, file transfers, SOCKS5/HTTP proxy tunneling, local and remote port forwarding, CIDR-range scanning, and FortiGate-specific configuration harvesting and credential decryption.
  • An auto-mode flag enables autonomous operations, automatically running a predefined command sequence upon initial infection.
  • Active exploitation dates back to at least July 2026 and remains ongoing. Analysis of attacker files revealed up to 30,000 targeted IP addresses with 178 confirmed PivotC2 victim sessions, with the majority located in the United States.
  • Analysis of files confirmed full network intrusions against two US-based targets, with tactics, techniques, and procedures (TTPs) pointing to a Russian-speaking, financially motivated cybercrime operator.

CVE-2025-25249 – Heap-Based Buffer Overflow in cw_acd Daemon

CVE-2025-25249 (Critical – CVSSv3: 9.8 as per NVD) is a heap-based buffer overflow vulnerability in the FortiOS and FortiSwitchManager cw_acd daemon. It allows remote, unauthenticated attackers to execute arbitrary code or commands via specially crafted requests. Fortinet uses CAPWAP (Control and Provisioning of Wireless Access Points) to enable FortiGate firewalls to centrally manage wireless access points. The daemon listens on UDP port 5246 (CAPWAP Control) for incoming control traffic. The affected versions include FortiOS 7.6.0-7.6.3, 7.4.0-7.4.8, 7.2.0-7.2.11, 7.0.0-7.0.17, 6.4 (all versions), and FortiSwitchManager 7.2.0-7.2.6 and 7.0.0-7.0.5. Published on January 13, 2026, Fortinet has already released patches and workarounds. STRU identified active exploitation dating back to at least July 2026.

Vendor filtering in SOCRadar's Vulnerability Intelligence module. Filter by Fortinet or any vendor in your stack to see what is weaponized, exploited, and trending right now.

Vendor filtering in SOCRadar’s Vulnerability Intelligence module. Filter by Fortinet or any vendor in your stack to see what is weaponized, exploited, and trending right now.

Exploitation in the Wild

The attack chain relies on an exploit binary (fortirun.bin) that highly likely targets CVE-2025-25249. Threat actors used Bash and Python scripts to automate the exploitation loop, attempting multiple passes to ensure success. fortirun.bin exploits the vulnerability, opens a reverse shell via Node.js, and passes a Base64-encoded payload for execution. The supporting scripts supply arguments to fortirun.bin, including the target FortiGate IP, CAPWAP Control port (default 5246), attacker listener IP and port, execution timeouts, and the final Base64-encoded payload. Details on the exploit tool are provided in the Appendix.

Contents of exploit.sh

Contents of exploit.sh

The initial payload passed alongside the exploit is a one-liner JavaScript stager that gets executed via Node.js. It downloads a second-stage payload to the compromised FortiGate (hxxps[://]146[.]103[.]99[.]177:8443/0c5b76709523), Base64-decodes the returned data chunks, and XOR-decrypts each byte using a specified key (pivot). It then saves the decrypted output to a temporary path (/tmp/.i.js) and executes it as a background process that persists even if the parent Node.js process exits. The executed script delivers PivotC2, a Node.js post-exploitation RAT designed specifically for compromised FortiGate appliances.

Sample Beautified payload.js

Sample Beautified payload.js

Recovered and Decrypted Next Stage (PivotC2 Client)

Recovered and Decrypted Next Stage (PivotC2 Client)

PivotC2: A FortiGate Post-Exploitation RAT

PivotC2 is a feature-rich post-exploitation framework purpose-built for FortiGate appliances running FortiOS. It supports interactive shells, file transfers, SOCKS5 and HTTP proxy tunneling, local and remote port forwarding, CIDR-range port scanning, and FortiGate-specific configuration harvesting with automated AES credential decryption.

It also features an auto-mode flag for fully autonomous operations. When enabled, every new client connection automatically triggers configuration harvesting, credential decryption, network extraction, and port scanning across both newly discovered and predefined internal IP ranges, all without requiring manual operator intervention. The codebase includes detailed inline comments and usage guidance, pointing toward potential AI assistance during development. The recovered version (0.2.3) indicates the tool is in an early development phase.

The RAT employs a classic reverse-shell architecture. The client always initiates the outbound connection to the C2 server, bypassing inbound firewall rules. All communication traverses a single TLS socket using a lightweight binary framing protocol that multiplexes named channels structurally resembling SSH’s channel model. On the backend, the C2 server manages session states, operator CLI interactions, and active service lifecycles (such as SOCKS5 proxies, port forwarding, and stagers).

PivotC2 Architecture

PivotC2 Architecture

Client C2 Communication

C2 Protocol

All C2 communication travels over a single TLS connection using a compact binary frame protocol. Each frame starts with a 4-byte big-endian unsigned integer indicating total payload length (including the 1-byte message type field), followed by a 1-byte message type and the payload itself. Except for CHANNEL_DATA (type 0x08), all frame payloads are structured UTF-8 JSON objects. CHANNEL_DATA instead prepends a 4-byte channel ID directly to raw binary data.

PivotC2 Protocol Frames

PivotC2 Protocol Frames

The full message type table is as follows. All types except 0x08 carry a JSON payload:

Hex Name Direction Key JSON fields
0x01 HELLO Client→C2 hostname, platform, arch, node_version, uptime, user, interfaces (sent on every TLS connect/reconnect)
0x02 HELLO_ACK C2→Client session_id (incremented sequentially for each victim)
0x03 HEARTBEAT Bidirectional ts (epoch timestamp in ms)
0x04 COMMAND C2→Client cmd_id, type, args
0x05 COMMAND_RESULT Client→C2 cmd_id, data (Base64), error, done
0x06 CHANNEL_OPEN Bidirectional channel_id, type, host / port / path / cmd
0x07 CHANNEL_OPEN_ACK Bidirectional channel_id, success, error
0x08 CHANNEL_DATA Bidirectional [binary: 4B cid (channel_id) + raw data] (no JSON fields)
0x09 CHANNEL_CLOSE Bidirectional channel_id
0x0A CHANNEL_EOF Bidirectional channel_id
0x0B LOG Client→C2 level, msg
0x0D TASK_RESULT Client→C2 task_id, status, data (Base64), error (background task output)

The protocol’s most notable feature is its SSH-inspired channel multiplexer. Over a single TLS connection, the framework supports an arbitrary number of independent, bidirectional byte streams (“channels”), each identified by a 32-bit integer.

The C2 opens channels on the client for interactive shells, command execution, file operations, and direct TCP forwarding. Conversely, the client opens channels to the C2 for reverse-port forwarding. Either side initiates streaming by sending CHANNEL_OPEN, awaiting a CHANNEL_OPEN_ACK, and then streaming data via CHANNEL_DATA frames. Sessions wind down with CHANNEL_EOF and close via CHANNEL_CLOSE. All channel abstractions are built on Node.js Duplex streams, allowing natural composition via .pipe().

C2 Commands & Channels

The client handles incoming actions by processing COMMAND frames. Each command executes a capability and returns a Base64-encoded COMMAND_RESULT. Short-running operations execute synchronously, while long-running or interactive tasks (such as interactive shells, file transfers, and continuous executions) open a dedicated streaming channel (CHANNEL_OPEN).

Supported commands and arguments:

  • exec (cmd): Executes a system command.
  • ls (path): Lists directory contents.
  • cat (path): Reads and returns file contents.
  • rm (path): Deletes a target file.
  • mv (src, dst): Moves or renames a file or directory.
  • mkdir (path): Creates a directory.
  • stat (path): Retrieves file status and metadata.
  • ifconfig: Retrieves network interface configurations.
  • netstat: Lists active network connections.
  • ps: Lists running processes and their execution arguments.
  • uname: Returns a system telemetry JSON including hostname, platform, arch, release, type, node, uptime, loadavg, totalmem, freemem, cpus, and kernel.
  • portscan (host [IP/CIDR], ports [ranges]): Performs port scans and maps open ports per host.
  • dns (host): Resolves domain names.
  • listen (port): Opens a reverse-forward listener. Inbound TCP connections open a forward channel back to the C2.
  • stop_listen (port): Terminates a reverse-forward listener.
  • harvest (files[] or none): Batch-collects sensitive files. Without arguments, it harvests default FortiGate configuration files (/data/config/sys_global.conf.gz and /data/config/global_system_interface.gz). When triggered alongside server credential harvesting (creds command, detailed later on the C2 server), it targets VDOM configurations (/data/config/sys_vd_root+root.conf.gz) containing encrypted (ENC) password hashes and /data/etc/fsv_sync.dat containing the AES-128-GCM device secret key.
  • task_start (task_id, type, args): Registers and executes a background task.
  • kill: Removes infection artifacts from disk and terminates the RAT process.
  • sleep (seconds): Adjusts the heartbeat ping interval.

Supported Channels:

Type Opened by Behavior
shell C2 → client Spawns an interactive shell via /bin/sh or the first available shell (bash, ash, dash, or busybox).
exec C2 → client Executes a command and streams output continuously through the channel.
upload C2 → client Streams file data over the channel, which the client writes directly to disk.
download C2 → client Streams requested file data from the client back to the C2.
direct C2 → client Instructs the client to connect to a target host:port on its internal network; handles SOCKS5 and HTTP proxy connections.
forward client → C2 Handles reverse forwarding. When an internal connection hits the client’s listener, the client opens a channel routing back to the C2’s localhost:localPort.

C2 Server Capabilities

Stager & Reconnection

The PivotC2 server includes an integrated stager (stager.js) designed to serve client source code that is Base64-encoded and XOR-encrypted using a specific keyword (i.e. pivot). The endpoint URL path serving the stager uses the first 12 hex characters of the PivotC2 client code’s SHA-256 hash (/0c5b76709523). Configuration parameters like target C2 IP/port, heartbeat intervals, jitter settings, and TLS certificate-pinning hashes are embedded directly within the client binary.

Sample PivotC2 Configuration Inside the Client’s Contents

Sample PivotC2 Configuration Inside the Client’s Contents

The RAT client is designed for persistent connectivity. A global uncaughtException handler suppresses unexpected errors to prevent crashes. If a TLS connection drops, the client enters an exponential backoff reconnect loop with configurable jitter until connection to the C2 is re-established. The session health is monitored dynamically: the client sends a HEARTBEAT frame every 30 seconds by default (configurable via the sleep command), and the C2 server runs an automated sweep timer every 15 seconds. If a client fails to send a heartbeat within 90 seconds, the C2 server marks the session as dead and terminates it.

Session Binding

Each accepted connection initializes a Session object that tracks connection state, pending commands, and background task statuses. Sessions are maintained within a key-value Map using sequential integer IDs.

C2 Operator Commands

Operators interact with active sessions via an interactive CLI (console.js). The console handles session switching, proxy management, and device-specific post-exploitation actions.

C2 Command and Arguments Behavior
sessions Lists all active client sessions.
interact <id> Enters an interactive session prompt. Supports standard client commands alongside creds (which harvests and decrypts ENC password hashes from the FortiGate device).
socks5 <id> <port> [host] Initializes a local SOCKS5 proxy routed through the specified client session.
http <id> <port> [host] Initializes an HTTP proxy routed through the specified client session.
fwd_local <id> <lport> <rhost> <rport> [lhost] Sets up local port forwarding (C2 listens locally and routes traffic through the client).
fwd_remote <id> <rport> <lhost> <lport> Sets up remote port forwarding (the client listens on its host and routes connections back to C2).
services Lists all active proxy and forwarding services.
stager <host> Outputs a single-line command stager configured for the target host.
stop <service_id> Terminates an active proxy or port-forwarding service.
exit Shuts down the C2 server application.

Example Commands Run by the Attackers

Example Commands Run by the Attackers

Proxy & Tunneling

SOCKS5 proxy (proxy/socks5.js): When initiated, the C2 server listens locally for inbound connections (e.g., from tools like proxychains). Upon receiving a CONNECT request, the server issues a CHANNEL_OPEN frame of type direct to the client. The client executes net.connect(host, port) on its local network and pipes the TCP stream through the multiplexed TLS channel. The SOCKS5 client on the C2 therefore sees transparent access to any host reachable from the FortiGate, with no additional tunneling overhead.

HTTP proxy (proxy/http.js): Supports both HTTPS CONNECT requests and absolute-URI plain HTTP requests through the same channel multiplexing design. Port forwarding is implemented as two variants: local forwarding, in which the C2 server opens a local listening port and instructs the client to forward connections to a target internal destination, and remote forwarding, in which the client executes a listen command to open a port on the compromised device and forward incoming connections back to the C2 server.

Configuration Harvesting & Credentials Decryption

PivotC2 automates the extraction and decryption of sensitive FortiGate system artifacts. Combining the harvest and creds commands (or running through Auto-Mode) dumps key files into a /loot/<ip>/harvest directory:

  • /data/config/sys_global.conf.gz (Global System Configuration)
  • /data/config/global_system_interface.gz (Network Interface Configurations)
  • /data/config/sys_vd_root+root.conf.gz (Compressed VDOM Configuration containing encrypted ENC XXXXXX credentials)
  • /data/etc/fsv_sync.dat (Device-specific secret file containing the AES-128-GCM key)

Code comments reference an unrecovered module (fortidecrypt.js) designed to automatically identify and decrypt ENC-formatted passwords using two distinct cryptographic schemes:

  • AES-256-CBC with a hardcoded key embedded in the source. Fields encrypted with this method carry a known magic trailer (Yf267vE@) that the decryptor uses for auto-detection. This technique is described in fortitool.
  • AES-128-GCM with a key derived from the device-specific fsv_sync.dat file. Fields that lack the CBC magic trailer potentially fall through the GCM decryption. The CBC path decrypts without access to the device secret, whereas the GCM path requires collecting fsv_sync.dat as well.

Successful decryption yields plaintext VPN pre-shared keys (PSKs), SSL-VPN user credentials, wireless PSKs, LDAP bind credentials, and administrator accounts. These credentials can be reused across the target organization’s infrastructure, enabling lateral movement beyond the FortiGate itself.

Auto-Mode: Autonomous Post-Exploitation Pipeline

When launched with the –auto flag, the C2 server initiates a fully automated, five-stage post-exploitation pipeline for every new client connection. The pipeline begins with the standard HELLO handshake to register client telemetry. It then executes the harvest and creds commands to collect the core FortiGate configurations, VDOM settings, and fsv_sync.dat file.

Once these artifacts are retrieved, the C2 server decrypts stored credentials and uses forticonfig.js to extract internal network interfaces from global_system_interface.gz. Finally, it launches background port scans targeting common ports (22, 80, 389, 443, 445, 902, 1433, 3389, and 5432) across both the newly discovered subnets and predefined internal IP ranges (10.0.0.0/24, 10.0.1.0/24, 172.16.0.0/24, 192.168.0.0/24, and 192.168.1.0/24). This enables mass-scale FortiGate compromise where the attacker simply lets devices beacon in and the C2 extracts credentials and maps internal networks autonomously.

PivotC2 Auto-Mode Flow

PivotC2 Auto-Mode Flow

Victimology

Analysis of the threat actor’s files revealed a target list of over 30,000 FortiGate IP addresses, 178 of which were confirmed to be exploited and infected with PivotC2. Each victim session recorded infection metadata, including the session ID, hardware name, OS architecture, Node.js version, victim IP address and port, and uptime since the infection. Geographic distribution of the IP addresses shows the highest concentration of infections in the United States, followed by Chile, Colombia, and the United Kingdom.

Geographic Distribution of PivotC2 Compromised FortiGate Firewalls

Geographic Distribution of PivotC2 Compromised FortiGate Firewalls

Threat Actor Profiling

Additional tools and operational outputs were recovered from intrusions impacting two US-based organizations. The attack lifecycle relies on exploiting CVE-2025-25249, deploying PivotC2, establishing internal network tunnels, conducting host discovery, extracting browser credentials, moving laterally, and exfiltrating data. Analyzed tool outputs show heavy reliance on AI tools across multiple stages of the intrusion, including the automated generation of command output reports.

Multiple comments in Russian were observed. The exfiltration tactics and active discovery of storage and backup infrastructure support a high-confidence assessment pointing to a Russian-speaking financially motivated cybercrime operator. Additionally, the actors seem to also target further known vulnerabilities like CVE-2024-47575 (FortiManager), CVE-2026-35273 (PeopleSoft Enterprise PeopleTools) and CVE-2024-26304 (ArubaOS).

Additional Tools / Operation Behavior
ldapdomaindump Performs Active Directory enumeration via LDAP; dumps domain computer accounts, groups, group policies, trust relationships, and user lists.
obfs4proxy / lyrebird Configures a local SOCKS5 proxy (port 9050) to route operational C2 traffic through specific Tor pluggable transports and bridges.
russh Utilizes a Rust-based SSH binary to establish reverse-SSH relays across internal targets.
SoftPerfect Network Scanner Conducts subnet discovery and network host profiling.
Registry Modification
fDenyTSConnections = 0
DisableRestrictedAdmin = 0
Enables Remote Desktop Protocol (RDP) and configures RestrictedAdmin mode to allow pass-the-hash authentication over RDP.
Browser Credential Extraction Dumps saved credentials from Google Chrome and Microsoft Edge; impersonates tokens to extract DPAPI master keys and decrypt stored secrets.
run.ps1 Execution Downloads a binary payload from the PivotC2 server, applies single-byte XOR decryption (key: 0xAB), decodes the Base64 layer, and executes process injection (OpenProcess, VirtualAllocEx, WriteProcessMemory) into svchost.exe.
S3 Exfiltration Tooling Packages and exfiltrates Microsoft Exchange mailbox files (.pst) to attacker-controlled Wasabi S3 storage buckets.

Mitigation & Recommendations

Threat actors continuously target perimeter edge devices for initial access due to their public exposure and frequent lack of robust monitoring or telemetry logging. To defend against the PivotC2 campaign, organizations deploying Fortinet products should immediately execute the following mitigation steps:

  • Exposure Mitigation: Restrict external access by disabling the fabric service on external interfaces or implementing a local-in policy to drop incoming UDP traffic on CAPWAP Control ports (5246-5249) in accordance with Fortinet’s PSIRT advisory.
  • Threat Hunting: Inspect exposed FortiGate appliances via the FortiOS CLI for established connections to known indicators of compromise (IoCs) and audit the filesystem for abnormal Node.js artifacts:
    • Check active sessions to known C2 IP addresses
      • diagnose sys session filter daddr 146.103.99.177
      • diagnose sys session filter daddr 46.151.29.58
      • diagnose sys session list
    • Inspect host directories for PivotC2 artifacts
      • fnsysctl ls -la /tmp/.i.js
      • fnsysctl ls -la /tmp/
    • Check running processes for malicious Node.js execution
      • fnsysctl cat /proc/self/status
      • diagnose sys process list | grep node
  • Credential Rotation: If suspicious activity or PivotC2 artifacts are detected, assume all appliance configuration files have been exfiltrated. Immediately rotate all administrative passwords, SSL-VPN user credentials, LDAP bind secrets, wireless PSKs, and IPSec pre-shared keys defined within the FortiGate configuration.
  • Vulnerability Remediation: Upgrade all FortiOS and FortiSwitchManager instances to the following fixed versions to remediate CVE-2025-25249:
    • FortiOS: 7.6.4, 7.4.9, 7.2.12, 7.0.18 (or higher)
    • FortiSwitchManager: 7.2.7, 7.0.6 (or higher)

Appendix: Technical Analysis of fortirun.bin

fortirun.bin is a two-stage, self-extracting exploit framework compiled as a Linux x86-64 ELF binary. Upon execution, the outer binary decompresses a ZStandard-compressed (zstd) payload stored within its .rodata section. This unpacks 18 bundled artifacts into memory: a complete Python 3 runtime interpreter (CPython), associated Python libraries, and a secondary inner binary (also named fortirun.bin). The inner binary houses two compiled Python modules: fortipwn.py and fortirun.py, which collectively execute an unauthenticated Remote Code Execution (RCE) attack against the CAPWAP daemon on FortiGate and FortiAP devices running vulnerable FortiOS v7.4.x builds.

Exploit Tool Flow

Exploit Tool Flow

Module 1: fortipwn.py – CAPWAP Exploit Engine

This module implements the core exploitation primitives, targeting the CAPWAP daemon listening on UDP port 5246.

Step 1 – Fingerprinting & ASLR Bypass

The exploit initiates by sending CAPWAP Discovery Request UDP packets crafted with a randomized MAC address. Upon receiving the Access Controller’s response, it extracts two identifier fields:

  • hw_rev – The hardware revision identifier.
  • sw_ver – The firmware version string (e.g., FGT60F-v7.4-build2731).

The extracted identifiers are queried against a hardcoded firmware compatibility table covering 13 FortiGate firewall models and 2 FortiAP wireless access point models running vulnerable FortiOS 7.4 releases (builds 7.4.0 through 7.4.8):

Targeted Model and Firmware Build
FGT40F v7.4-build2726 FGT40F v7.4-build2731 FGT60F v7.4-build2726
FGT60F v7.4-build2731 FGT61F v7.4-build2731 FGT70F v7.4-build2731
FGT80F v7.4-build2731 FGT80F v7.4-build2726 FGT90G v7.4-build2731
FG100F v7.4-build2731 FG100F v7.4-build2726 FG101F v7.4-build2731
FG120G v7.4-build2731 FP221E (any detected build) FP231F v7.4-build0634

If the target firmware is absent from the table, execution halts with the error [-] unsupported version. If matched, fortipwn.py loads targeted offset profiles for the specific build:

  • image_base: Base RAM address of the daemon’s executable code section, obtained from the discovery response to defeat ASLR (Address Space Layout Randomization).
  • data_rw_base: Base address of the writable memory section, likewise obtained from the discovery response.
  • free_list_mp2: Memory pool structure offset targeted for heap corruption.
  • br_x16_gadget: Offset of an ARM64 ROP pivot instruction (br x16).
  • execvp: Address of the system call used to execute arbitrary code.

ASLR normally randomizes memory locations on every boot to block predictable code execution. However, because the CAPWAP discovery response leaks live memory pointers, fortipwn.py dynamically calculates image_base and data_rw_base at runtime. This neutralizes ASLR protection, allowing the script to derive absolute memory addresses for all required gadgets and functions.

Step 2 – Heap Grooming

Before triggering the heap overflow, the exploit performs heap grooming, a technique where an attacker sends carefully chosen packets to manipulate how the target process allocates memory. The goal is to force the server to arrange memory blocks in a precise, predictable layout, ensuring that the target buffer sits directly adjacent in memory to the structure the attacker wants to corrupt.

The CAPWAP daemon (cw_acd) relies on a pre-allocated memory pool structure named free_list_mp2 (a doubly-linked list of fixed-size chunk allocations) to satisfy frequent small memory requests instantly, without asking the operating system for new memory every single time. Nodes within free_list_mp2 are linked to the next and previous blocks via two pointer fields, called fd (forward/next) and bk (backward/previous). This linked structure is what the exploit corrupts.

To groom the heap, the exploit sends a series of malicious CAPWAP Add Station control messages. In a legitimate network, this type of message is sent when a wireless client (such as a laptop or phone) associates with an access point where the controller allocates memory to track that client. Here, the exploit weaponizes this mechanism. Each crafted Add Station packet carries a station descriptor field filled with 56 bytes of the character A (0x41), forcing the daemon to allocate a 72-byte memory chunk (56 bytes of data + 16 bytes of internal chunk overhead) from free_list_mp2. This 72-byte chunk falls into a fixed allocation size class in the daemon’s memory pool.

By sending many such packets in a loop, the exploit exhausts fragmented gaps within the memory pool, forcing subsequent chunks into contiguous, predictable memory addresses. The result is a heap layout where the exploit-controlled buffer sits immediately before the target free_list_mp2 free list node, setting up the overflow.

Heap Grooming in hex dump

Heap Grooming in hex dump

Step 3 – Heap Overflow & Memory Corruption

Once the heap is groomed, the exploit dispatches a weaponized CAPWAP Image Data control message (a message type intended for firmware and binary updates).

The packet is constructed using the Python struct format string <IIQQQQQ (two 32-bit integers + five 64-bit integers = 48 bytes total), and contains:

Field Size Value
int32[0] 4 bytes Offset to the target write location (relative to image_base)
int32[1] 4 bytes Control/size field
int64[0] 8 bytes image_base: Runtime base address
int64[1] 8 bytes data_rw_base: Writable section base address
int64[2] 8 bytes free_list_mp2: Address of the memory pool target node
int64[3] 8 bytes br_x16_gadget: ARM64 ROP pivot instruction address
int64[4] 8 bytes execvp: Final code execution target

The message element header fields (me_type and me_sz) misdeclare the payload size to be smaller than the data actually written. This causes the daemon to copy excess data into the target buffer. Specifically, the overflow writes 16 bytes of attacker-controlled data beyond the end of the 56-byte target buffer, corrupting the adjacent free_list_mp2 node in memory:

  • next_ptr (8 bytes) overwrites free_list_mp2.fd (forward pointer).
  • prev_ptr (8 bytes) overwrites free_list_mp2.bk (backward pointer).

Step 4 – Arbitrary Write & Control Flow Hijack

By corrupting both fd and bk with attacker-controlled values, the exploit executes a safe-unlink / House of Einherjar-style attack against the daemon’s custom doubly-linked free list.

During the unlink operation (which removes a chunk from the free list) these overwritten pointers transform into a write-what-where primitive, granting the ability to write an arbitrary 8-byte value to any memory address. The exploit leverages this primitive to overwrite a function pointer or return address immediately before it is executed by the daemon.

Doubly-linked List Corruption

Doubly-linked List Corruption

Modern operating systems enforce W^X (Write XOR Execute, also known as Data Execution Prevention or DEP), which ensures memory regions are writable or executable, but never both. This prevents direct code injection into the heap.

To bypass W^X, the exploit uses Return-Oriented Programming (ROP). Instead of injecting new code, it chains together small fragments of existing, already-executable code (“gadgets”) already present in the daemon’s binary:

  • Target Gadget (br_x16_gadget): An ARM64 instruction sequence ending in blr x16 or br x16 (branch to register x16).
  • Execution: This gadget acts as an ROP pivot, redirecting the instruction pointer to the address stored in x16, which the exploit sets to the target function (execvp).

Step 5 – Node.js Reverse Shell

The hijacked execvp call launches a Node.js reverse shell, establishing an outbound connection from the compromised FortiGate back to the attacker. Because Fortinet utilizes Node.js within its management plane, the runtime is natively present on the system. The exploit targets this pre-existing environment, eliminating the need to drop new software onto the victim device.

Node.js Reverse Shell

Node.js Reverse Shell

Module 2: fortirun.py – Payload Delivery

The fortirun.py module manages the final stage of payload delivery once fortipwn.py establishes the initial reverse shell connection. When provided with a payload argument, it transmits the Base64-decoded Node.js script directly into the target’s interactive Node.js REPL (node -i) and appends a unique completion marker consisting of __FORTIPWN_DONE_ followed by a random 16-character hexadecimal string. The script then captures the resulting output stream from the REPL until the marker is detected, at which point it prints the output and cleanly closes the connection. The module accepts arguments for the target FortiGate IP, the attacker’s listening IP and port, the local payload path, and configurable timeout and retry intervals.

MITRE ATT&CK TTPs

Tactic Technique ID Technique Name Description
Resource Development T1587.004 Develop Capabilities: Exploits The threat actors crafted an exploit tool for CVE-2025-25249.
Initial Access T1190 Exploit Public-Facing Application The threat actors exploit CVE-2025-25249 on edge FortiGate devices.
Execution T1059.007 Command and Scripting Interpreter: JavaScript The threat actors execute Node.js-based PivotC2 through Node.js.
Execution T1059.004 Command and Scripting Interpreter: Unix Shell PivotC2 spawns background shells (/bin/sh, bash, ash, dash, busybox).
Execution T1059.001 Command and Scripting Interpreter: PowerShell The threat actors executed run.ps1 to download, decrypt, and inject a binary payload into a system process.
Defense Evasion T1036.009 Masquerading: Break Process Trees PivotC2 payload executes as an independent background process to remain running even if the parent Node.js application terminates.
Defense Evasion T1055.002 Process Injection: Portable Executable Injection The threat actors executed run.ps1 to perform process injection using OpenProcess, VirtualAllocEx, and WriteProcessMemory into svchost.exe.
Defense Evasion T1027 Obfuscated Files or Information PivotC2 stager and clients used Base64 encoding, custom XOR encryption keys (pivot), and obfuscated JavaScript constructs.
Defense Evasion T1070.004 Indicator Removal: File Deletion PivotC2 features a kill command to automatically wipe infection artifacts and terminate the RAT on demand.
Discovery T1046 Network Service Discovery The threat actors conducted port scanning (via PivotC2’s portscan command or auto-mode, SoftPerfect Network Scanner) across CIDR blocks and specific internal IP ranges.
Discovery T1087.002 Account Discovery: Domain Account The threat actors leveraged ldapdomaindump to dump AD users, groups, trust relationships, and domain policies.
Discovery T1082 System Information Discovery The threat actors issued uname, stat, and ps PivotC2 commands to gather host system metadata, architecture, memory states, kernel types, and running processes.
Discovery T1016 System Network Configuration Discovery The threat actors issued ifconfig, netstat, and dns PivotC2 commands to inspect local interface bindings, network connections, and perform DNS lookups.
Discovery T1083 File and Directory Discovery The threat actors issued PivotC2 commands ls and cat for file system browsing, alongside the automated batch harvesting of configuration files.
Lateral Movement T1021.001 Remote Services: Remote Desktop Protocol The threat actors modified registry values (fDenyTSConnections = 0 and DisableRestrictedAdmin = 0) to enable RDP with Restricted Admin mode on compromised hosts.
Lateral Movement T1021.004 Remote Services: SSH The threat actors utilized russh (Rust SSH) executables to establish reverse-SSH relays inside the target network.
Collection T1003.001 Credentials from Password Stores: Credentials from Web Browsers The threat actors dumped Chrome and Edge browser credentials.
Collection T1552.001 Unsecured Credentials: Credentials In Files PivotC2 collects FortiGate system configurations (/data/config/*, /data/etc/fsv_sync.dat) to harvest and decrypt stored ENC credentials (VPN PSKs, SSL-VPN passwords, LDAP bind creds).
Collection T1114.001 Email Collection: Local Email Collection The threat actors collected Exchange .pst email archive files for exfiltration to Wasabi S3 buckets.
Command and Control T1071.001 Application Layer Protocol: Web Protocols PivotC2 communication used HTTPS/TLS-wrapped channels, custom HTTP proxy capabilities, and downloading stagers via web requests.
Command and Control T1573.002 Encrypted Channel: Asymmetric Cryptography PivotC2 multiplexed C2 communication over a single TLS-encrypted socket with support for certificate pinning.
Command and Control T1090.001 Proxy: Internal Proxy PivotC2 includes built-in functionality for multi-channel SOCKS5 and HTTP proxying (proxy/socks5.js, proxy/http.js) to tunnel network traffic through infected nodes.
Command and Control T1090.002 Proxy: External Proxy The threat actors configured a local SOCKS5 proxy (port 9050) using obfs4proxy and lyrebird to route internal traffic through Tor bridges.
Command and Control T1090.003 Proxy: Multi-hop Proxy PivotC2 supports local and remote port forwarding (fwd_local, fwd_remote, listen), creating reverse-forward channels back to the C2 infrastructure.
Command and Control T1105 Ingress Tool Transfer PivotC2 embeds upload and download channel commands to transfer binary files, stagers, and scripts onto infected systems.
Exfiltration T1567.002 Exfiltration Over Web Service: Exfiltration to Cloud Storage The threat actors used S3 upload scripts and executables to exfiltrate Exchange .pst email archive files directly to Wasabi S3 buckets.

IoCs

Host Indicators

Type Indicator Description
SHA256 2d338ffc8cc80293575c6800c059e33eb41e967907c20ba7687b2231c50837db fortirun.bin – CVE-2025-25249 exploit
SHA256 cc7f0660d56405cbdff157033d3e35305f063e62efaff6501d11e6a34e7bd151 payload.js – PivotC2 stager
SHA256 eb4d8aab4e687839c5478a7a3819b0a7a857555ed50fc159c026e99764a0c8a0 payload2.js – PivotC2 stager
SHA256 fe7da807a2b37a2bbd8c27830a9acc0d86ad8128f38489c493873c7e410c0408 payload_new.js – PivotC2 stager
SHA256 d99fa14f5e7dfe17e437f167f3f9550ebeda496960710dde81d41748bd7749e4 PivotC2 client (decoded) on 46.151.29[.]58:8443
SHA256 005e6014fb8fd47249691756f5af3b3d53bfae82df88a71277e53e13fe94cb9f PivotC2 client (encoded) on 46.151.29[.]58:8443
SHA256 550f99193f9e90d93b70af1ab050a2d44f1830259ea165568dafc518e761c589 PivotC2 client (decoded) on 146.103.99[.]177:8443
SHA256 08fa6abac9c132deff4f120a7fcfe5bf17c797b87dbbc3f261d5cf0c077c0a2e PivotC2 client (encoded) on 146.103.99[.]177:8443
SHA256 a9bea5f89984d47dd60216b0a0b064e8c7e8057e0c10509faa4a2d8d641eb73b PivotC2 client (decoded) on 146.103.99[.]177:9443
SHA256 1bf2c5976f2abbe147ae7be140ed69af2c25092f563786400aecd0231229be19 PivotC2 client (encoded) on 146.103.99[.]177:8443
SHA256 c25a27b506fbae62010caf2abff699df5c94d29f056fa7ccfb5f3170d917c8cb run.ps1 – Process injection script
SHA256 d4911736986cf8affb29106fb8e8b74e00e52d5f762dce9025f2cfe431cf2140 payload.b64 – Injected payload

Network Indicators

Type Indicator Description
IP Address 46[.]151[.]29[.]58 PivotC2 Node
IP Address 146[.]103[.]99[.]177 PivotC2 Node
IP Address & Port 45[.]138[.]16[.]182:9130 obfs4proxy TOR Bridge
IP Address & Port 89[.]217[.]174[.]207:9001 obfs4proxy TOR Bridge

Frequently Asked Questions

What Is CVE-2025-25249?

CVE-2025-25249 (High – CVSSv3: 7.4) is a heap-based buffer overflow vulnerability in the FortiOS and FortiSwitchManager cw_acd daemon, the process that handles CAPWAP traffic used to centrally manage wireless access points. The daemon listens on UDP port 5246 (CAPWAP Control), and specially crafted requests allow remote, unauthenticated attackers to execute arbitrary code or commands. Fortinet published the advisory on January 13, 2026, along with patches and workarounds.

Which Versions Are Affected?

FortiOS 7.6.0-7.6.3, 7.4.0-7.4.8, 7.2.0-7.2.11, 7.0.0-7.0.17, and 6.4 (all versions), plus FortiSwitchManager 7.2.0-7.2.6 and 7.0.0-7.0.5. The recovered exploit tool itself carries offset profiles for 13 FortiGate models and 2 FortiAP models running FortiOS 7.4 builds 7.4.0 through 7.4.8, so the immediate in-the-wild exposure is concentrated in that range.

Is CVE-2025-25249 Being Exploited in the Wild?

Yes. SOCRadar identified active exploitation dating back to at least July 2026, and the activity is ongoing. Attacker files show a target list of more than 30,000 FortiGate IP addresses, of which 178 were confirmed to be exploited and infected.

What Is PivotC2?

PivotC2 is a Node.js remote access trojan built specifically for post-exploitation on compromised FortiGate appliances. It is delivered after successful exploitation of CVE-2025-25249 and maintains a persistent outbound TLS connection to a C2 server, which bypasses inbound firewall rules. The recovered version (0.2.3) indicates the tool is still in an early development phase, and its inline comments and usage guidance point to AI-assisted development.

What Can PivotC2 Do on a Compromised Device?

  • Interactive access: Spawns shells via /bin/sh, bash, ash, dash, or busybox and streams command output over dedicated channels.
  • File operations: Uploads, downloads, lists, reads, moves, and deletes files.
  • Tunneling: Provides SOCKS5 and HTTP proxying plus local and remote port forwarding into the internal network.
  • Discovery: Runs CIDR-range port scans, interface and connection enumeration, and DNS lookups.
  • Credential theft: Harvests FortiGate configuration files and decrypts stored ENC passwords.
  • Cleanup: A kill command wipes infection artifacts and terminates the process on demand.

What Does Auto-Mode Change?

Launching the C2 server with the –auto flag turns every new infection into an autonomous pipeline. On connection, the server harvests configurations, decrypts credentials, extracts internal networks from the interface configuration, and launches background port scans against both discovered subnets and predefined internal ranges (10.0.0.0/24, 10.0.1.0/24, 172.16.0.0/24, 192.168.0.0/24, and 192.168.1.0/24). No operator interaction is required, which makes mass-scale FortiGate compromise practical.

Which Credentials Are at Risk?

PivotC2 collects the global system configuration, network interface configuration, VDOM configuration containing ENC-formatted secrets, and the device-specific fsv_sync.dat file. Decryption uses AES-256-CBC with a hardcoded key for fields carrying a known magic trailer, and AES-128-GCM with a key derived from fsv_sync.dat for the rest. Successful decryption yields VPN pre-shared keys, SSL-VPN user credentials, wireless PSKs, LDAP bind credentials, and administrator accounts, all of which are reusable elsewhere in the environment.

Who Has Been Targeted?

Infections are concentrated in the United States, followed by Chile, Colombia, and the United Kingdom. Two US-based organizations were compromised beyond the firewall itself, with internal tunneling, host discovery, browser credential theft, lateral movement, and confirmed data exfiltration to attacker-controlled cloud storage.

Who Is Behind the Campaign?

SOCRadar assesses with high confidence that this is a Russian-speaking, financially motivated cybercrime operation. The assessment rests on Russian-language comments in the recovered tooling, the exfiltration tradecraft, and active discovery of storage and backup infrastructure. The same operators also show interest in other known vulnerabilities, including CVE-2024-47575 (FortiManager), CVE-2026-35273 (PeopleSoft Enterprise PeopleTools), and CVE-2024-26304 (ArubaOS).

Is This Campaign Related to FortiBleed?

They are separate operations. The recovered PivotC2 dataset shares no C2 infrastructure, tooling, or victim overlap with the FortiBleed operation attributed to the INC and Lynx ransomware groups. What the two have in common is the playbook: Russian-speaking crews treating FortiGate appliances as the cheapest route into an enterprise, harvesting and decrypting stored configuration secrets, then reusing those credentials to move inland. FortiBleed ended in ransomware deployment, while the PivotC2 intrusions observed so far stop at data exfiltration. Organizations that hardened their perimeter after FortiBleed and rotated the credentials stored on those devices are already covering most of the exposure this campaign relies on.

How Can Organizations Detect PivotC2?

Check exposed appliances from the FortiOS CLI for sessions to known C2 addresses (46.151.29[.]58 and 146.103.99[.]177) with diagnose sys session filter daddr followed by diagnose sys session list. Audit the filesystem for the staged payload at /tmp/.i.js using fnsysctl ls -la /tmp/, and look for unauthorized Node.js execution with diagnose sys process list | grep node. Because the RAT runs inside a runtime that legitimately ships with FortiOS, process names alone are not a reliable signal.

What Should Organizations Do Now?

  • Patch: Upgrade FortiOS to 7.6.4, 7.4.9, 7.2.12, or 7.0.18 and FortiSwitchManager to 7.2.7 or 7.0.6, or higher.
  • Reduce exposure: Disable the fabric service on external interfaces or apply a local-in policy dropping inbound UDP traffic on CAPWAP Control ports (5246-5249).
  • Rotate credentials: If artifacts or suspicious sessions are found, treat the full configuration as exfiltrated and rotate administrative passwords, SSL-VPN credentials, LDAP bind secrets, wireless PSKs, and IPSec pre-shared keys.
  • Hunt internally: Assume the firewall was a pivot point and review internal hosts for RDP registry changes, reverse-SSH relays, browser credential access, and outbound transfers to cloud storage.

Does Patching Remove PivotC2?

No. Patching closes the exploitation path but does not remove an implant that is already running or the credentials that were already harvested. Any appliance with confirmed artifacts should be treated as compromised, rebuilt or restored from a known-good state, and followed by full credential rotation.