Shittr was a Twitter clone and part of ENOWARS 3. It provides user accounts, which can be registered using a username and password. Each user can then send messages, which can either be private or public, as well as set a status message in their profile. A user can also see and like other user’s messages. Private messages of other users are visible, but only appear as long base64-encoded strings.
Vulnerability 1: Shared Secret Key
The first step was understanding how messages are encoded in the code base. After posting a message, the user sees a long base64-encoded string.
We follow the code to see how the message is turned into a base64-encoded string.
First we identified the place where the encoding happens.
db.sh there is a function called
create_shit, which contains this line:
This looks already quite promising, as we have the base64-encoding in there as well as an encryption function.
We can locate the encryption and decryption routines in
enc function also base64-encodes its output, thus our final message which we see as the user is actually double base64-encoded.
openssl call looks normal, so let’s see where the encryption key is coming from.
config.sh we spot these lines:
The code specifies the path to the keyfile and generates a new one, if it does not exist yet.
The keyfile is located at
./ro/static/enc.key and contains
Let’s try to decode the message
V2dFQVhDWlpERjF0TVMwT1B3PT0= from above into its plaintext again:
The error message stems from
openssl and searching for
iv in the codebase does not yield any useful information.
However, we find a binary named
openssl in the
Using this binary we receive the expected string
openssl binary behaves differently depending on the length of its
It only works, if the argument is short, but calling it with a long absolute path results in garbage output, which cannot be decoded anymore.
This weird behaviour cost us a lot of time until we understood why our decoding did not work anymore.
openssl binary is a large stripped executable, such that we could not check why this was happening or if there are any backdoors in the binary itself.
We noticed, that the
enc.key file was not generated by the
config.sh script, but instead was installed during the installation of the service from the
This means, all the teams are sharing the same secret key, and we can use our secret key to decode any private message.
Our first exploit simply looks for all messages on a user’s profile page. If the message looks like a flag, or we can decode the base64-encoded message to a flag, then we submit it. Accessing the profile page also gives us access to the status messages each user can set.
The main difficulty in writing the exploit was that Shittr returns the HTTP status code 1337 for its content pages, but
requests does not support custom status codes and instead throws an exception.
Fortunately, account registration and login was still possible by setting
requests, such that it does not hit the 1337 status code, but already receives the
We then download the website using
curl, which can handle the status code, by passing the cookie to a new
The exploit consists of these steps:
- Account registration
- Account login
- Retrieve a list of all users using the
- For each user, visit the profile page at
/@usernameand try to decode all messages.
We patched the vulnerability by replacing our secret key with a new one, generated with the same command from above:
After a restart, all new messages in the system were encrypted with a different key.
Vulnerability 2: Admin Access
After some time into the CTF, we noticed that other teams started spamming our system with usernames of the form
Tracing this pattern through the codebase, we find this function:
It tests if the username contains
admin via a regex match and if the variable
DEBUG contains any value.
The username matches, as we have seen, and in the
DEBUG=1 is set.
Being an admin does not give you access to the private messages of a user.
You only get access to the
It contains the list of all requested websites and information about account registration and login.
The interesting information in the log are lines like
Session is 708423527839774631739000000000.
These contain the secret which is used to calculate the
auth cookie and thus the user authentication.
Everyone in possession of these values can forge session cookies.
The gameserver never used any accounts with
admin in their names.
We disabled all admin functionality by replacing
ADMIN=0 in the entire code base, thus blocking the log as an information leak.
Vulnerability 3: Weak Authentication Cookies
Based the on the findings of the previous vulnerability, we analyze the session code more.
Session handling is part of the
db.sh file, with this function being the most relevant one:
This code generates a random value and uses that to generate the session ID (the
auth cookie), by calculating the md5 over parts of it.
However, the code only takes the first three digits (
This mean that in total there are only 1000 different
auth cookies, from
The exploit for this vulnerability simply brute forces all possible values for the
We do this in a loop for every team and retrieve the page with the current user’s messages at the
This was a very successful exploit which gave us many points over the rest of the game.
We fixed this problem by using the full value of
rand as input to
md5sum, thus drastically increasing the entropy.
Unknowns: Including Images
There was another functionality in the Shittr service which made us suspicious, but which we could not figure out how to exploit.
While sending a message you could embed an image link.
If the image link pointed to a http(s) site and contained the string
.png, then the service would download the image and replace the URL with an
The suspicious code can be found in
db.sh in the
The code reads the first URL matching the regex
(http.?)?://[\S\[\]:.png]+ and if it contains
.png, then the if-condition at the top of the while loop will be passed and the file downloaded.
The regex has multiple irregularities.
For one, it allows you to fully omit the protocol part and start with
curl does support URLs without a protocol part, but only if the
:// part is also missing.
As such, it did not seem possible to leverage this detail to our benefit.
Second, the character set
[\S\[\]:.png] contains a
. which is unescaped, thus allowing any character.
This makes all the other specified character unnecessary and the whole group could just be replaced with a simple
.png also does not need to appear at the end of the URLs.
Any URL is fine, as long as it appears somewhere, even the fragment identifier is an option.
What this feature allows us to do, is download an arbitrary file to another team’s VM. We could use it for a Denial-of-Service attack, downloading large files and filling up the disc. If we could find a way to execute them or use them instead of the included templates, we could use it as part of a remote code execution. However, we did not find any such flaw during the CTF.
After fixing these three vulnerabilities, we fixed the flag leaks in our service. At one point, we lost three additional flags over three game rounds, but could not figure out which exploit was used against us. After the three game rounds, the exploits stopped and we did not lose any more defensive points.