RaaS
SSRF using Gopher protocol leads to tampering of Redis key-value store
Last updated
SSRF using Gopher protocol leads to tampering of Redis key-value store
Last updated
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-k
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.
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 gist. 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 protocol, but this wouldn't work because urllib.parse
was updated to strip newline characters.
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!!!}