RaaS

SSRF using Gopher protocol leads to tampering of Redis key-value store

Challenge

Description: Since everything is going online, I decided to make an easy Requests as a Service Bot to make life easier, but I seem to have messed up oops!!!

Author: Capt-karrow-up-right

Solution

Local File Inclusion

We are able to enter a URL for the server to request. A pretty trivial LFI vulnerability exists as a result of SSRF, allowing us to view files using the file:// protocol.

Since we're provided with the Dockerfile, we know that the server code is in /code/app.py.

Thus, we can request file:///code/app.py to view the server code.

Immediately, we see that a Redis database is used. The hostname is redis, and it is listening on port 6379.

If a POST request is received, the Requests_On_Steroids function, which we will analyze later, is used to fetch the URL. Otherwise, the <userID>_isAdmin key in the Redis database is checked. If the value is "yes", then the flag is shown in the response.

It appears that we would have to overwrite our <userID>_isAdmin value. Since we have a SSRF vulnerability, we might be able to leverage it to communicate with the Redis instance.

Redis Over Gopher

In main.py, we can see that the Requests_On_Steroids function supports the Gopher protocol. Using Gopher, we can communicate with any TCP server (but of course, we would have to follow the service's higher-layer protocol).

However, instead of gopher://, we must use inctf:// instead.

In modules/Gophers.py, we find the GopherAdapter code.

With some Googling, we can find out that the Gopher adapter was actually modified from this GitHub gistarrow-up-right. I wanted to find out if any changes was made from the original script, so I diff-ed the two scripts.

Interestingly, a line of code was modified to remove /_ in the URL's path.

Ideally, we would send multi-line input using the RESP protocolarrow-up-right, but this wouldn't work because urllib.parse was updated to strip newline charactersarrow-up-right.

Redis also offers inline commands, allowing us to send our commands directly, but without the above change, our inline commands (parsed.path) would still look like this:

The /SET command is unrecognized, leading to an error. Instead, we can leverage the replacement using the following payload:

The path, when replaced, would be

which sets our <userID>_isAdmin value to "yes".

This gives us the flag: inctfi{IDK_WHY_I_EVEN_USED_REDIS_HERE!!!}

Last updated