Stealthily Backdooring CMS Through Redis’ Memory

Having covered Redis’ pentesting basics, let’s cut to the chase. Once again suppose that we are performing a penetration test against the 175.12.97.0/24 range and we come across the 175.12.97.97 machine this time.

We can scan the 175.12.97.97 machine for any exposed Redis instances, as follows.

# nmap -p 6379 175.12.97.97

It looks like a Redis instance is exposed! Let’s interact with it…

First, let’s check if the exposed Redis instance requires authentication, as follows.

The exposed Redis instance is unprotected!

Let’s get some more information about this Redis instance with the help of the redis-cli tool, as follows.

175.12.9797:6379> INFO

We are dealing with a newer version of Redis this time.

In addition, let’s be more thorough this time around and scan all ports of the 175.12.97.97 machine, as follows.

# nmap -sS -p- 175.12.97.97

We will have to interact with each one of the identified ports to be sure we didn’t miss something important.

Surprisingly, port 1330 is actually a port of an SSH server, residing in an Ubuntu-based machine. We identified that, as follows.

# telnet 175.12.97.97 1330

Now, if we read Redis’ security page, we will come across the following statement.

“Internally, Redis uses all the well known practices for writing secure code, to prevent buffer overflows, format bugs and other memory corruption issues. However, the ability to control the server configuration using the CONFIG command makes the client able to change the working dir of the program and the name of the dump file. This allows clients to write RDB Redis files at random paths, that is a security issue that may easily lead to the ability to compromise the system and/or run untrusted code as the same user as Redis is running.”

To simplify things, the above means that we can “write” random files on Redis’ host (since we are dealing with an unprotected Redis instance). During our reconnaissance activities we identified that 175.12.97.97 features an SSH server. We can try writing our own SSH authorized key into the authorized_keys file in order to gain access, as follows.

As discussed, we want to write our own authorized key into the authorized_keys file.

To do so, we will need to identify the correct path. During port scanning, we identified that we are dealing with an Ubuntu-based machine. On Ubuntu-based machines the file we are looking for usually resides on /home/<username>/.ssh.

If you recall, we are dealing with a newish version of Redis. This means, that the dofile() enumeration technique will probably not help a lot. Fear not through, we can still use Redis as an oracle to identify an existing path, as follows.

Execute the below to install a Python interface to the Redis key-value store.

# pip install redis

Create a usernames.txt file, containing various usernames. For example:

Then, save the below as a .py script in the same directory where you saved usernames.txt.

Execute the script you just saved.

# python how_you_named_the_script.py

We identified the correct path! Let’s continue writing our own authorized key.

Crafting the blob to be dropped via Redis

Getting Redis’ current configuration (already set through the script we executed previously)

Updating Redis’ configuration.

Writing/dropping our own authorized key.

Connecting through SSH using our own authorized key.

We are in as user!

Let’s think about persisting on that host.

Leaving an SSH authorized key behind is too noisy. In addition, we don’t want to issue too many commands through the Secure Shell, to avoid being detected. What we can do to persist on that host is leverage Redis, once again.

Remember, that any blob we sent so far, first goes into Redis’ memory and it is then written to the specified path. So, we are talking about some quite stealthy payload transferring process.

Unfortunately, we are actually using the RDB format under the hood and this fact causes an issue. The output will be in binary format and subsequently some strings may “break” (common webshells or MSF payloads can’t be easily transferred through Redis). Thankfully, at eLearnSecurity, we identified that one can successfully write (drop) JavaScript code through Redis (BeEF anyone?).

First, let’s find a safe place for our backdoor. During our reconnaissance activities, we identified port 8081 to be open. By browsing http://175.12.97.97:8081/administrator/ , we came across a Joomla installation. Let’s backdoor it ! A nice place to write (drop) our backdoor through Redis is /var/www/html/joomla/templates/beez , where Joomla’s beez template resides (detailed reconnaissance/enumeration helped us identify that).

The backdooring procedure, consists of the following steps.

Spin up BeEF.

Write the HTML backdoor and transfer it through Redis’ memory, as follows.

If you now browse to http://175.12.97.97:8081/templates/beez/index.html you will see yourself being hooked through BeEF’s hook.

From now on, every user visiting a web page that loads the backdoored template (almost all of them do, this is why templates exist), will get hooked by BeEF!

You can now delete your dropped authorized key and go away.