Security
Jun 11, 2026

CVE-2026-40987: Spring Integration Remote-File Synchronizer File Write

How a remote-file synchronizer that writes a server-supplied filename under localDirectory without canonicalization lets a malicious FTP/SFTP/SMB server plant files anywhere on the client

Give me the TL;DR
CVE-2026-40987: Spring Integration Remote-File Synchronizer File Write
For Qualys admins, NES for .NET directly resolves the EOL/Obsolete Software:   Microsoft .NET Version 6 Detected vulnerability, ensuring your systems remain secure and compliant. Fill out the form to get pricing details and learn more.

On June 10, 2026, the Spring team disclosed CVE-2026-40987, a High-severity (CVSS 3.1 base score 7.1, vector AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L) arbitrary file write in Spring Integration's remote-file support. The flaw is an improper-canonicalization path traversal (CWE-22): the inbound remote-file synchronizer writes a server-supplied filename under the configured localDirectory without canonicalizing it, so a malicious or compromised FTP, SFTP, or SMB server can return entries containing ../ sequences and write attacker-controlled content to arbitrary locations outside that directory. It affects Spring Integration 5.5.0 through 7.0.4 across every supported branch, plus older unsupported releases. OSS fixes shipped in 6.5.9, 7.0.5, and 7.1.0; for the 5.5.x, 6.3.x, and 6.4.x branches there is no OSS fix available.

Running an unsupported version of Spring? See NES for Spring for drop-in security patches on EOL Spring Integration branches.

What is CVE-2026-40987?

CVE-2026-40987 is a path traversal vulnerability, classified under CWE-22 (Improper Limitation of a Pathname to a Restricted Directory), in Spring Integration's remote-file inbound support. The affected component is the remote-file synchronizer that backs the inbound file adapters for FTP, SFTP, and SMB: the piece responsible for fetching files from a remote server and writing local copies under a configured localDirectory.

In normal use, an application configures an inbound adapter (for example, an SFTP inbound channel adapter) that connects to a remote server, lists files, and synchronizes them to a local working directory for downstream processing. The synchronizer takes the filename reported by the remote server and uses it to construct the local destination path.

The defect is that the remote-supplied filename is joined to localDirectory and written without canonicalization. A filename is normally expected to be a simple leaf name, but nothing forces that. If the remote server returns a name such as ../../../../etc/cron.d/payload or a Windows path that climbs out of the directory, the synchronizer resolves it relative to localDirectory and writes there, escaping the intended boundary. The trust assumption (that the remote server reports benign, well-formed filenames) is the assumption that breaks: the client is trusting input it does not control.

Severity and exploit conditions

The Spring advisory rates this High. The published CVSS 3.1 vector is AV:N/AC:H/PR:L/UI:R/S:C/C:L/I:H/A:L, which computes to a base score of 7.1.

The preconditions are specific, which is why Attack Complexity is High. Exploitation requires all of the following:

  • The application uses Spring Integration's remote-file inbound support (FTP, SFTP, or SMB) with a synchronizing inbound adapter.
  • The application synchronizes from a remote server that an attacker controls or has compromised, or that can otherwise be induced to return crafted filenames.
  • The local process has filesystem permissions to write to the traversal target.

This is a supply-chain-shaped trust problem rather than a classic public-facing input bug. The remote server is usually treated as a trusted integration partner, so the filenames it returns receive little scrutiny. That is exactly the assumption the vulnerability exploits, and it is why the affected surface is easy to overlook in a threat model that focuses on user-facing inputs.

Root cause

The remote-file synchronizer builds the local destination path by joining the remote-supplied filename to the configured localDirectory, then writing the fetched content to that path. The filename is used as-is: there is no canonicalization step, and no check that the resolved path still resides within localDirectory after normalization.

Conceptually, the vulnerable flow looks like this:

// Remote server reports a filename for each entry to synchronize.
// The synchronizer joins it to the configured localDirectory and writes:
String remoteFilename = remoteEntry.getFilename();   // attacker-controlled
File local = new File(localDirectory, remoteFilename); // no canonicalization
writeContent(local, fetchedBytes);                     // escapes localDirectory

Because new File(localDirectory, remoteFilename) happily resolves ../ segments, a remote filename like ../../../../opt/app/config/application.properties lands wherever that relative path points, not under localDirectory. The fix introduces canonicalization and validation so that the resolved target must remain within the configured directory; any name that escapes is rejected before the write occurs.

The general lesson is the one path traversal keeps teaching: any time externally supplied data becomes part of a filesystem path, it has to be canonicalized and then validated against the intended root before the path is used. A filename from a remote protocol peer is externally supplied data, even when that peer is nominally trusted.

What an attacker can do

A successful write with attacker-controlled content and an attacker-chosen path is a high-integrity primitive. Concrete outcomes depend on what the application's process can write to, but commonly include:

  • Code or command execution via drop targets: writing to a location that is later executed or sourced, such as a scheduled-job directory (/etc/cron.d/), a startup script, a web-deployment directory, or a plugin/extension path the application loads.
  • Configuration tampering: overwriting an application's own config files (for example, application.properties or application.yml) to flip security settings, repoint datasources, or inject malicious values that take effect on the next load or restart.
  • Credential and trust-store poisoning: overwriting or planting key material, known_hosts, or trust-store files to weaken authentication or enable later man-in-the-middle steps.
  • Clobbering operational files: overwriting logs, lock files, or data files the process depends on, degrading integrity and availability.

The write happens with the privileges of the Spring Integration process, so the blast radius is bounded by that process's filesystem permissions. In containerized or least-privilege deployments the reachable targets are narrower; in deployments where the integration process runs with broad write access, the path from this bug to code execution is short.

Who is affected?

The vulnerability affects every supported Spring Integration branch from 5.5 onward, plus older unsupported releases. OSS fixes are available only for the actively maintained 6.5.x and 7.0.x lines (and the new 7.1.0). The 5.5.x, 6.3.x, and 6.4.x branches have reached the end of open source maintenance and receive no OSS fix.

If you are on 6.5.x or 7.0.x, upgrading to the patched release is the direct path. If you are on 5.5.x, 6.3.x, or 6.4.x, there is no open source patch for your branch: the only OSS-fixed releases are on newer major/minor lines, which means a version migration rather than a point upgrade. For teams that cannot migrate on the disclosure-day timeline, NES for Spring provides a secure drop-in replacement with this CVE remediated on the EOL branch.

Mitigation guidance

Defense-in-depth reduces blast radius but does not close the vulnerability. The synchronizer still performs the unsafe write; permission hardening only limits where that write can land. The fix is the patched code path, whether that arrives via an OSS upgrade on a maintained branch or via NES on an EOL branch.

Related CVEs

CVE-2026-40987 is the latest in a run of path-traversal and arbitrary-file-access issues across the Spring ecosystem, several of which follow the same root pattern (external input reaching a filesystem path without canonicalization):

Taking action

CVE-2026-40987 is a clean illustration of why EOL software compounds risk. The vulnerability reaches back to Spring Integration 5.5, but the open source project only shipped fixes on its maintained lines (6.5.9, 7.0.5, 7.1.0). Organizations on 5.5.x, 6.3.x, or 6.4.x have no open source patch for their branch: their only OSS option is a version migration, on the same day a working arbitrary-file-write primitive is now public.

If you run Spring Integration on a maintained line, upgrade to the patched release now. If you are on an EOL branch and a migration is not viable on this timeline, this is exactly the gap HeroDevs exists to close: NES for Spring delivers secure drop-in replacements for end-of-life Spring branches, including this CVE remediated on versions the open source project no longer maintains. Review your inbound FTP/SFTP/SMB integrations, confirm which Spring Integration version each service runs, and prioritize the ones synchronizing from servers outside your direct control.

Table of Contents
Author
Greg Allen
Chief Technology Officer
Open Source Insights Delivered Monthly