Spring Boot CVE-2026-40976: Quick Look at a Critical Actuator Authorization Bypass in Versions 4.0.0 Through 4.0.5

A brief summary of CVE-2026-40976, a CVSS 9.1 authorization bypass in Spring Boot 4.0.0 through 4.0.5 that leaves all Actuator endpoints unauthenticated under a specific dependency configuration, along with patch details and remediation guidance.

CVE Analysis

8 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-27

Spring Boot CVE-2026-40976: Quick Look at a Critical Actuator Authorization Bypass in Versions 4.0.0 Through 4.0.5
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A misplaced conditional check in Spring Boot's default security auto configuration silently disables all authorization rules for Actuator endpoints, letting any unauthenticated user pull environment secrets, heap dumps, and configuration properties with a single HTTP request. The flaw, tracked as CVE-2026-40976 with a CVSS 3.1 score of 9.1, affects Spring Boot versions 4.0.0 through 4.0.5 and was patched in the 4.0.6 release on April 23, 2026.

The vulnerability is particularly subtle because it only manifests under a specific but realistic dependency combination: a servlet based web application that relies on the default security filter chain, includes spring-boot-actuator-autoconfigure, and does not include the newer spring-boot-health module. This configuration commonly appears in custom Boot starters, slimmed down container images, or applications that intentionally exclude health endpoints to avoid conflicts.

Technical Information

Root Cause

CVE-2026-40976 is classified as CWE-862 (Missing Authorization). The vulnerability exists at the intersection of Spring Boot 4.0's modular dependency restructuring and its autoconfigured security defaults.

In Spring Boot 4.0, the health endpoint support was extracted from spring-boot-actuator-autoconfigure into a dedicated spring-boot-health module. The default web security configuration in ManagementWebSecurityAutoConfiguration.java used a ClassUtils.isPresent(...) check to determine whether the HealthEndpoint class existed on the classpath. The intent was straightforward: conditionally grant unauthenticated access (permitAll()) only to the health endpoint, while locking down everything else with anyRequest().authenticated().

The critical flaw was in where that conditional check was placed. In the vulnerable code, the entire authorizeHttpRequests block was nested inside the isPresent guard:

if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint", getClass().getClassLoader())) { http.authorizeHttpRequests((requests) -> { requests.requestMatchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); requests.anyRequest().authenticated(); }); }

When spring-boot-health was not on the classpath (meaning the HealthEndpoint class was absent), the entire authorization configuration was silently skipped. No rules at all. The default filter chain was registered without any authorization rule covering the Actuator path, leaving every exposed endpoint reachable anonymously.

Exploitation Conditions

For an application to be exploitable, all of the following conditions must be true simultaneously:

ConditionDescription
Application TypeMust be a servlet based web application
Security ConfigurationMust have no Spring Security configuration of its own and rely entirely on the default web security filter chain
Actuator DependencyThe classpath must include spring-boot-actuator-autoconfigure
Health DependencyThe classpath must NOT include spring-boot-health

If any single condition is false, the application is not vulnerable.

Attack Flow

An attacker targeting a vulnerable application would follow a straightforward path:

  1. Reconnaissance: The attacker identifies a Spring Boot application (often via response headers or error page formatting) and probes the /actuator base path. On a vulnerable instance, this returns a JSON document listing all available Actuator endpoints without requiring any authentication.

  2. Secret Exfiltration via /actuator/env: The attacker issues a GET request to /actuator/env, which returns the full application Environment including resolved property values such as database passwords, API keys, and JWT signing keys. A single request is sufficient for full credential compromise.

  3. Heap Dump Capture via /actuator/heapdump: The attacker requests /actuator/heapdump, which triggers a full JVM heap dump. This binary file contains live object state, in memory credentials, and active session tokens. Offline analysis of this dump can reveal secrets that are not present in environment properties.

  4. Log Manipulation via /actuator/loggers: The attacker can POST to /actuator/loggers to modify logging levels at runtime, either suppressing security audit logs to cover their tracks or enabling verbose logging to capture additional sensitive data flowing through the application.

  5. Architecture Mapping via /actuator/configprops: The attacker retrieves /actuator/configprops to obtain a complete view of all configuration properties, revealing application architecture, integration wiring, and downstream service endpoints that can be targeted next.

Impact Summary

EndpointExposed DataBusiness Impact
/actuator/envDatabase passwords, API keys, JWT signing keysFull credential compromise and secret exfiltration
/actuator/heapdumpLive JVM heap with in memory credentials and session tokensDeep data exposure enabling offline analysis
/actuator/configpropsAll configuration propertiesReveals application architecture and integration details
/actuator/loggersLogger configuration (read/write)Attackers can suppress security logs or enable verbose capture

The exposure of the environment and heap dump endpoints alone is typically sufficient for an attacker to escalate from anonymous network access to full credential compromise in a single request.

Patch Information

The Spring team addressed CVE-2026-40976 in Spring Boot 4.0.6, released on April 23, 2026. The fix was authored by Andy Wilkinson and committed under SHA 874f6294b91da18367b8b5ab7b2fad3fa23cfba6, directly closing GitHub issue #50188. The fix was also forward ported to the 4.1.x line via issue #50190 (milestone 4.1.0-RC1).

The Servlet Fix

The fix in ManagementWebSecurityAutoConfiguration.java restructures the nesting so the authorization lambda is always applied, and only the health specific permitAll() matcher is conditional:

- if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint", - getClass().getClassLoader())) { - http.authorizeHttpRequests((requests) -> { - requests.requestMatchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); - requests.anyRequest().authenticated(); - }); - } + http.authorizeHttpRequests((requests) -> { + if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint", + getClass().getClassLoader())) { + requests.requestMatchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); + } + requests.anyRequest().authenticated(); + });

This is a deceptively simple but critically important change: by pulling requests.anyRequest().authenticated() outside the isPresent check, every request is now guaranteed to require authentication regardless of whether the health module is available.

The Reactive Fix

The reactive WebFlux counterpart in ReactiveManagementWebSecurityAutoConfiguration.java had the opposite problem: it did not have any ClassUtils.isPresent guard, meaning it unconditionally tried to call healthMatcher() and additionalHealthPathsMatcher(). The fix wraps just the health specific permit rules inside the presence check, while ensuring anyExchange().authenticated() always applies:

http.authorizeExchange((exchanges) -> { - exchanges.matchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); + if (ClassUtils.isPresent("org.springframework.boot.health.actuate.endpoint.HealthEndpoint", + getClass().getClassLoader())) { + exchanges.matchers(healthMatcher(), additionalHealthPathsMatcher()).permitAll(); + } exchanges.anyExchange().authenticated(); });

New Tests

The commit adds a dedicated test method securesEverythingElseWhenHealthIsAbsent() in both the servlet and reactive test suites. This test uses @ClassPathExclusions(packages = "org.springframework.boot.health.actuate.endpoint") to simulate the exact vulnerable configuration and confirms that all endpoints now correctly return 401 Unauthorized. Existing tests were also updated so that the HealthContributorAutoConfiguration, HealthContributorRegistryAutoConfiguration, and HealthEndpointAutoConfiguration classes are explicitly added only in tests that actually exercise health endpoint behavior.

The total change footprint is modest (121 additions and 52 deletions across 4 files) but the security impact is dramatic: it closes a full authorization bypass that left all endpoints unauthenticated under a specific but realistic dependency combination.

Affected Systems and Versions

The vulnerability affects Spring Boot versions 4.0.0 through 4.0.5.

To be vulnerable, an application must meet all four of the following conditions:

  1. It is a servlet based web application.
  2. It has no Spring Security configuration of its own and relies entirely on the default web security filter chain.
  3. Its classpath includes spring-boot-actuator-autoconfigure.
  4. Its classpath does not include spring-boot-health.

Applications using Spring Boot versions prior to 4.0.0 are not affected, as the modular split of the health endpoint into a separate module was introduced in the 4.0 line. Applications running Spring Boot 4.0.6 or later, or 4.1.0-RC1 or later, contain the fix.

Vendor Security History

The Spring Boot 4.0.6 release that patches CVE-2026-40976 also addresses three other vulnerabilities discovered in the same version range:

CVE IDVulnerability
CVE-2026-40970Elasticsearch auto configuration TLS hostname verification disablement
CVE-2026-40972DevTools remote secret comparison timing attacks
CVE-2026-40975Random value property source using weak PRNG
CVE-2026-40976Default security filter chain missing authorization rule

The bundling of four security fixes in a single release indicates an active security maintenance cycle. The Spring team disclosed the vulnerability and shipped the patch on the same day (April 23, 2026), demonstrating a coordinated disclosure and rapid response process.

References

Detect & fix
what others miss

Security magnifying glass visualization