GITS 2015 CTF 'aart' writeup
aart was a web challenge worth 200 points at the 2015 GITS CTF. There were several ways to solve it, three of which will be described here.
General
This challenge consisted of a website that allowed the creation of user accounts, login to those accounts as well as submitting ASCII art and voting for it. Furthermore there was a link on top of the page that provided the whole source of the website (except connect.php which contained the MySQL connection information).
There are three files of interest to us, the login script, the register script and the database scheme:
login.php:
register.php
schema.sql:
The first information we obtain is that we can retrieve the flag by logging into an account which does not have isRestricted
set to TRUE. Upon further inspection we can see that the input goes through mysqli_real_escape_string sanitation. SQL injection would be possible, but only if the variables inside the SQL query are not surrounded by apostrophes. As it turns out, in all the files they are however, so SQL injection is not possible.
Looking at the register.php file, we can see two INSERT
s being executed. In the first, the new user account is simply created by inserting the username and password into the users
table. This will generate the id, as the id
column in users
is set to AUTO_INCREMENT (see schema.sql).
In the second INSERT
the privileges of this newly created account are set. For this a userid, isRestricted pair are inserted into the privs
table. The userid, however, is fetched in a subquery by comparing the username from the *$_POST variable to the one existing in users
table.
There are several ways to exploit this script, two involving race conditions and one involving truncation of usernames. During the CTF we used the latter one, but we are going to describe all three of them.
Exploit 1: Username truncation
This exploit uses the fact, that data is getting truncated by MySQL if it is above the max length of the field’s datatype.
Vulnerability
Looking at schema.sql, we note that the username field has datatype TEXT
, in MySQL the TEXT
datatype has a capacity of 65535 bytes (see MySQL storage requirements). Everything that is longer than that, will just be truncated! Now the exploit is straight forward:
Exploit
Register a new account with a username length > 65535 characters, let’s say we choose username = 'A'*65535 + 'B'
. The newly created account has its username set to ‘A’*65535, since the ‘B’ will simply be ignored due to truncation. In the subquery, the id
is selected by comparing the chosen username (i.e. ‘A’*65535 + ‘B’) to usernames in the database. But since the username inserted into the database was truncated, this subquery will return no results! Therefore there will be no entry in privs
table for our newly created account. We login using the truncated username and retrieve the flag:
this is a key
is the actual flag.
Finally, a simple PoC script that will register a random username with length > 65535 and then use the truncated username to login and fetch the flag:
Exploit 2: Race condition - Double registration
In the register script there are two SQL queries that are executed one after the other with the second one depending on the first. This results in a race condition which we can exploit.
Vulnerability
The first way to exploit this vulnerability is to register twice before the privileges are inserted into priv
table. We can see in schema.sql, that the username field is not declared UNIQUE and hence we can have two rows with identical usernames. When the subquery in the second query will try to fetch the id
for the given username, it will retrieve two rows as it does not have a LIMIT 1
statement. The whole query will then fail with an Subquery returned more than 1 row
error and no privileges will be inserted.
Exploit
We need to register twice with the same username, for this we can use the following PoC python script:
Executing that script will output the flag:
[*] Registered twice
[x] fail
[*] Registered twice
[*] flag: 'this is a key'
Exploit 3: Race Condition - Login before privileges inserted
Similar to Exploit 2, we can also try to login before the privileges are inserted, again exploiting the race condition. The following PoC script will do exactly that and output the flag if successful:
We retrieve the flag again:
[x] fail
[x] fail
[*] flag: 'this is a key'
While this exploit works, it is not persistent as Exploit 1 and Exploit 2 are. Meaning that if we were to login with the created account again, it would be restricted now and not display the flag.
You can download all three PoC scripts here: Exploit 1 Exploit 2 Exploit 3