Container Escape in Cassandra
How Container Escape Manifests in Cassandra
Container escape in Cassandra environments typically occurs through misconfigured JMX/RMI endpoints, insecure native library loading, and improper volume mounting. Cassandra's architecture creates unique attack surfaces that attackers can exploit to break out of containerized deployments.
The most common vector involves Cassandra's JMX/RMI interface, which by default listens on all network interfaces and requires no authentication. When exposed through container ports, an attacker can connect to the JMX port (7199) and execute arbitrary Java code using the java.lang.Runtime.exec() method. This allows execution of host system commands, effectively escaping the container.
Another Cassandra-specific vector involves the use of native libraries for compression and encryption. Cassandra loads native libraries like libsnappy and libaes through System.loadLibrary(). If these libraries are compromised or loaded from untrusted paths, they can execute arbitrary code with the same privileges as the Cassandra process, potentially accessing the host filesystem.
Volume mounting misconfigurations create additional escape paths. Cassandra containers often mount data directories to persist data across restarts. If these mounts are configured with overly permissive permissions or mounted from sensitive host directories, an attacker who compromises the Cassandra process can write files to the host filesystem, including /proc or /sys to manipulate kernel parameters.
// Vulnerable Cassandra JMX configuration in docker-compose.yml
version: '3.8'
services:
cassandra:
image: cassandra:4.0
ports:
- '7199:7199' # JMX port exposed without auth
- '9042:9042' # CQL port
environment:
- HEAP_NEWSIZE=128M
- MAX_HEAP_SIZE=1G
volumes:
- ./data:/var/lib/cassandra
- /etc/passwd:/etc/passwd:ro # Dangerous bind mount
The /etc/passwd bind mount in this example demonstrates a critical misconfiguration. An attacker who escapes the container can modify host system files, potentially adding new user accounts or escalating privileges. Additionally, if the container runs with --privileged flag or cap-add for dangerous capabilities like SYS_ADMIN, the escape becomes trivial.
Cassandra-Specific Detection
Detecting container escape vulnerabilities in Cassandra requires examining both configuration files and runtime behavior. Start by analyzing the Cassandra configuration for exposed JMX endpoints and insecure native library paths.
Check the cassandra-env.sh file for JMX configuration:
# Look for these insecure patterns in cassandra-env.sh
JMX_PORT=7199
LOCAL_JMX=no
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=false"
These settings disable authentication and SSL for JMX, creating a major attack surface. A secure configuration should enable authentication with a password file and require SSL/TLS.
For runtime detection, scan for exposed JMX ports and verify native library loading paths:
# Check for exposed JMX ports
netstat -tlnp | grep 7199
# Check loaded native libraries
jcmd VM.native_memory summary
jcmd VM.system_properties | grep -i snappy
middleBrick's black-box scanning approach is particularly effective for detecting these vulnerabilities without requiring internal access. The scanner tests for unauthenticated JMX access, attempts to load malicious native libraries, and verifies container isolation boundaries.
The LLM/AI security module in middleBrick can also detect if your Cassandra deployment includes AI/ML components that might be vulnerable to prompt injection attacks, which could be chained with container escape vulnerabilities for more sophisticated attacks.
For comprehensive analysis, middleBrick's OpenAPI/Swagger spec analysis can identify API endpoints that might be used in conjunction with container escape attempts, such as administrative interfaces or debug endpoints that provide system-level access.
Cassandra-Specific Remediation
Securing Cassandra against container escape requires multiple layers of defense, starting with proper JMX configuration and extending to container runtime hardening.
First, secure the JMX interface by enabling authentication and SSL:
# cassandra-env.sh - secure JMX configuration
JMX_PORT=7199
LOCAL_JMX=yes
# Enable authentication with password file
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=true"
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password"
# Require SSL/TLS
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=true"
JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl.need_client_auth=true"
Create the password file with restricted permissions:
# /etc/cassandra/jmxremote.password
monitorRole
controlRole
# Set proper permissions
chmod 600 /etc/cassandra/jmxremote.password
chown cassandra:cassandra /etc/cassandra/jmxremote.password
For native library security, use explicit paths and integrity verification:
// Java code to securely load native libraries
public class SecureNativeLoader {
private static final String LIBRARY_DIR = "/usr/lib/cassandra/native/";
public static void loadLibraries() {
String[] libraries = {"snappy", "aes", "lz4"};
for (String lib : libraries) {
try {
// Verify library exists and is owned by cassandra user
File libFile = new File(LIBRARY_DIR + "lib" + lib + ".so");
if (!libFile.exists() || !libFile.canRead()) {
throw new RuntimeException("Missing or unreadable library: " + lib);
}
// Load with security manager if available
System.load(LIBRARY_DIR + "lib" + lib + ".so");
} catch (Exception e) {
System.err.println("Failed to load " + lib + ": " + e.getMessage());
// Log and continue with alternatives or fail gracefully
}
}
}
}
Container runtime hardening is equally important:
# Secure docker-compose.yml
version: '3.8'
services:
cassandra:
image: cassandra:4.0
ports:
- '9042:9042' # Only expose necessary CQL port
environment:
- HEAP_NEWSIZE=128M
- MAX_HEAP_SIZE=1G
volumes:
- ./data:/var/lib/cassandra:rw
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- SETGID
- SETUID
read_only: true
tmpfs:
- /tmp:noexec,nosuid,size=100m
user: "999:999" # Run as non-root user
This configuration drops all capabilities except those absolutely necessary, mounts volumes with read-write only where needed, and uses a non-privileged user. The no-new-privileges flag prevents privilege escalation even if the process is compromised.
Finally, implement network segmentation and monitoring:
# Network policies to restrict Cassandra communication
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: cassandra-network-policy
spec:
podSelector:
matchLabels:
app: cassandra
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: cassandra-client
ports:
- protocol: TCP
port: 9042
egress:
- to: []
ports:
- protocol: TCP
port: 9042
This network policy ensures Cassandra can only communicate with authorized clients on the CQL port, blocking any attempts to use JMX or other administrative interfaces from unauthorized sources.