BSides Canberra 2018 CTF Write-Up: Birdie Yellow

This is a write-up of the Birdie Yellow challenge from the BSides Canberra 2018 CTF. This challenge was worth 175 points.

The challenge

The aim of the Birdie series is to pose as the “admin” user, without being able to simply enter “admin” into the username field. Birdie Yellow is no exception to this. When trying to enter admin as the username, we are greeted with the below friendly error.

Entering “admin” as the username directly.

Recon Time

Let’s start by inspecting the source for this page, perhaps it will drop some hints as to how to solve this challenge.

<!doctype html>
<html>
<head>
<style>
html, body {height: 100%;background-color: black;height: 100%;margin: 0px;padding: 0px;color: white;font-family: courier, monospace;text-align: center;}h1 {margin-top: 5%;}a {color: green;}input {padding: 10px;}
</style>
<title>Inebriated Alibis</title>
</head>
<body>
<h1><font color="Gold">Inebriated Alibis</font></h1>
<p><form action="/login" method="post">
Username: <input name="username" />
<br /><br />
<input type="submit" value="Login / Register" />
</form>
</p>
</body>
</html>

Hmm… there really isn’t anything to go off there. Time to dive a little deeper.

Input Fuzzing

Now its time to try and fuzz the input to see what messages or other behaviours occur from various inputs. From this we can note that a valid input will send us back to the root of the server with a welcome message.

If we have a further look at some other inputs, we note that some special characters are blacklisted. For example, trying any string with a ‘\’ (escape character), will also be blocked with the error message “Please.” Interestingly, other commonly filtered characters such as ‘"’ and ‘{ or }’ have not been filtered in a similar way here, and are allowed through. More on this later…

But what about other elements of the page? Good question. From the valid input “letmein” which was tried earlier, some cookies appear from the server with the response welcome page. Specifically, a cookie names session_datalooks quite appealing.

Fuzzing Cookies

The cookie session_data contains a simple string:

%7B%22username%22%3A%22letmein%22%7D

This string looks rather like URL encoded characters… A quick pass through an online URL encoder/decoder reveals the encoded characters:

{"username":"letmein"}

AHAH! (some ways in)

The cookie seems to be storing a JSON string. From a quick look at the source code, there is nothing manipulating this on the client side because there is no JavaScript on the page. That must mean that this string is manipulated and managed purely by the server. This is good news for us.

Let’s have a play around with the value for the “username” parameter using a cookie editor extension. If this is changed to “admin” by us, and then the page refreshed, nothing changes. The page still thinks we are logged in under the “letmein” username. This is not looking good…

Going back a step, how is this cookie created? It must be constructed from the contents of the input field “username” before being passed to the rest of the server code for setting the rest of the session up — like the rendering of the username on the welcome page. Perhaps there is a way of manipulating the JSON string from the username field and using that to get “admin” recognised as the username to be parsed on the server?

JSON Parsing

Now for some background: given the way in which most languages parse JSON strings, if the same parameter exists twice within the one string in the same scope, the last encountered value for that parameter will be used.

For example, if I have the following JSON string…

{"mood":"happy","current_task":"fuzzing","mood":"frustrated"}

… when it is parsed, mood will first be set to the value “happy”, but then subsequently be overridden by the value “frustrated”. Now let’s see if we can use the same attack vector on Birdie Yellow.

The Attack

Before getting ahead of ourselves, lets see if we can indeed use this attack vector to our advantage with this challenge. To do this, we can build a payload to change the username using the inherent function of most JSON parsers (as explored above).

Back on the login page, we can take advantage of knowing that the output JSON is {“username”:”<the username we type in>”} to build what is in essence a JSON injection payload.

The Payload

Due to whatever is typed into the username field being (as long as it passes the basic filter) placed directly into the JSON value field, we can use a payload which closes the first username value ( ), and then add our own repetition of the username parameter with our malicious value (admin). This means our payload will look like this…

letmein", "username":"admin

Remember that this will be slotted into the value of the pre-existing username field, making the final full JSON string which the server then interprets our malicious valid JSON:

{"username":"*letmein", "username":admin*"}

NOTE that above I have placed a pair of ‘ *’ to denote the start and end of the injected payload. These are not part of the actual final string.

Let’s give that a go on the login page now.

Booyah!

Nice!

If we have a look at that session_data cookie from earlier again, we can see that out payload has been put to work well.

%7B%22username%22%3A%22letmein%22%2C+%22username%22%3A%22admin%22%7D
>>>{"username":"letmein", "username":"admin"}

Recap

This challenge involved a JSON string injection which was able to bypass a simple filter which screened for the word “admin” exclusively in the username field and a simple filter for some escape characters such as “\”.

By taking advantage of the way in which JSON parsers evaluate the values for properties which occur multiple times within the same scope, the injection was able to re-write the username property’s value. This was, in this instance, evaluated after the server performed its initial filter checks as listed above, which is the reason this attack is able to work.


It is always a good idea to take a checklist of web recon techniques into a challenge like this one. This would make solving a challenge like this much simpler as it would immediately be clear from different inputs what was happening on the server, and how the input could manipulate that.

Thank you once again to Elttam for providing these challenges, it was certainly a great experience, and lots was learnt from tackling the various challenges.