CVE-2026-22745

No items found.
Affects
Spring Framework
in
Spring
No items found.
Versions
>=3.0.0 <=5.3.47, >=6.0.0 <=6.0.23, >=6.1.0 <=6.1.26, >=6.2.0 <=6.2.17, >=7.0.0 <=7.0.6
Exclamation circle icon
Patch Available

This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.

Overview

Spring Framework is the foundational framework of the Spring ecosystem, providing comprehensive support for developing Java enterprise applications. Both of its web stacks, Spring MVC (Servlet) and Spring WebFlux (reactive), can be configured to serve static resources directly from the filesystem through the resource-handler pipeline, which wraps each candidate file in a FileSystemResource and asks it whether it is readable before streaming it to the client.

A medium-severity vulnerability (CVE-2026-22745) has been identified in that pipeline on Windows platforms. The FileSystemResource.isReadable() method invokes java.io.File.canRead() before checking whether the file exists. On Windows, File.canRead() is implemented through a native call that walks the path, consults the security descriptor, and can be slow to resolve for pathological path strings. Because the resource handler calls isReadable() synchronously on the serving thread, an unauthenticated remote attacker can flood the server with requests for non-existent or slow-to-resolve paths under a static-resource mapping and keep each HTTP connection blocked in the expensive native call, exhausting the server's worker threads or event-loop slots and denying service to legitimate users.

Per OWASP: "The Denial of Service (DoS) attack is focused on making a resource (site, application, server) unavailable for the purpose it was designed. There are many ways to make a service unavailable for legitimate users by manipulating network packets, programming, logical, or resources handling vulnerabilities, among others." In this case, the attacker exploits asymmetric resource consumption in a filesystem API call to tie up the server's request-handling threads with cheap, malicious requests.

The CVSS v3.1 base score for this vulnerability is 5.3 (Medium) with vector AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L. The attack is network-accessible, has low attack complexity, and requires neither privileges nor user interaction, and its impact is confined to a low availability loss with no confidentiality or integrity impact.

The vulnerable isReadable() implementation predates the project's migration from Subversion to Git in 2008 and has shipped in every tagged Spring Framework release from 3.0.0 (December 2009) onward. This issue affects versions 3.0.0 through 5.3.47, 6.0.0 through 6.0.23, 6.1.0 through 6.1.26, 6.2.0 through 6.2.17, and 7.0.0 through 7.0.6 of Spring Framework.

Details

Module Info

Vulnerability Info

The vulnerability is in the FileSystemResource class in the spring-core module, specifically in its isReadable() method.

An application becomes vulnerable when all of the following are true:

  • The application is using Spring MVC or Spring WebFlux
  • The application is serving static resources from the file system
  • The application is running on a Windows platform

When those conditions are met, Spring MVC's ResourceHttpRequestHandler (or the Spring WebFlux reactive equivalent) asks the configured PathResourceResolver chain for a Resource corresponding to an incoming request path. For filesystem-backed locations the chain returns a FileSystemResource, and the request pipeline then calls isReadable() to decide whether to proceed with serving the file.

Prior to the fix, isReadable() ran File.canRead() as its first check and only then tested for directory-ness, without first verifying that the file exists:

@Override
public boolean isReadable() {
    return (this.file != null ? this.file.canRead() && !this.file.isDirectory() :
            Files.isReadable(this.filePath) && !Files.isDirectory(this.filePath));
}

On POSIX systems File.canRead() is backed by access(2), which returns cheaply for non-existent paths. On Windows the equivalent native call walks the reparse-point and device-namespace resolution layers and consults the security descriptor of the target. For attacker-crafted path strings such as those referencing Windows device namespaces, reserved DOS device names, deeply nested UNC-style prefixes, or long \\?\-prefixed paths, the Windows filesystem API can take substantially longer to resolve than a simple existence probe, blocking the serving thread for the duration of the call.

Because isReadable() is invoked synchronously on the servlet thread in Spring MVC and on the reactive event-loop thread in Spring WebFlux, an unauthenticated attacker can send a stream of concurrent requests for pathological paths under the static-resource mapping and keep each worker slot tied up in the expensive native call. The handler thread holds the HTTP connection open for the duration of the check, and once the pool is saturated the server stops accepting new work for legitimate clients. The attack is cheap for the client and expensive for the server, which is why the advisory categorizes the impact as availability only.

The fix adds this.file.exists() as the first clause of the File-branch check so that non-existent paths short-circuit before invoking canRead():

@Override
public boolean isReadable() {
    return (this.file != null ? this.file.exists() && this.file.canRead() && !this.file.isDirectory() :
            Files.isReadable(this.filePath) && !Files.isDirectory(this.filePath));
}

File.exists() is backed by a much cheaper Windows syscall and returns false immediately for paths that do not exist on the filesystem, which is the class of input attackers can generate freely. The NIO branch using Files.isReadable(this.filePath) was left unchanged because that method already combines existence and readability checks internally. The companion commit message makes the rationale explicit: "Resource#isReadable() is documented to imply existence. FileSystemResource should strictly follow that contract through a combination of File.exists() and File.canRead() calls, performing the former first since it does not require a native call underneath." Two unit tests were added in the same commit to assert the ordering: exists() is consulted first and canRead() is only invoked when exists() returned true.

Mitigation

Only recent versions of Spring Framework receive community support and updates. Older versions have no publicly available fixes for this vulnerability.

Users of the affected components should apply one of the following mitigations:

  • Upgrade to a currently supported version of Spring Framework.
  • Leverage a commercial support partner like HeroDevs for post-EOL security support through Never-Ending Support (NES) for Spring Framework.

Credits

  • Bocheng Xiang (@crispr) from Fudan University (finder)
Vulnerability Details
Severity
Level
CVSS Assessment
Low
>=0 <4
Medium
>=4 <6
High
>=6 <8
Critical
>=8 <10
Medium
ID
CVE-2026-22745
PROJECT Affected
Spring Framework
Versions Affected
>=3.0.0 <=5.3.47, >=6.0.0 <=6.0.23, >=6.1.0 <=6.1.26, >=6.2.0 <=6.2.17, >=7.0.0 <=7.0.6
NES Versions Affected
Published date
April 17, 2026
≈ Fix date
April 17, 2026
Category
No items found.
Vex Document
Download VEXHow do I use it?
Sign up for the latest vulnerability alerts fixed in
NES for Spring
Rss feed icon
Subscribe via RSS
or

By clicking “submit” I acknowledge receipt of our Privacy Policy.

Thanks for signing up for our Newsletter! We look forward to connecting with you.
Oops! Something went wrong while submitting the form.