The “Super Quick Logic Invitational” is a web-based logic game challenge that presents players with math and logic problems. Players must solve problems within a time limit to earn points, but the last challenge seems impossible! It simply asks “What is the flag for this CTF challenge?”
Initial Reconnaissance
First, let’s understand what we’re working with:
- Start the application
- Explore the main interface: Visit the site in your web browser to see the main page
- Look for hints: The page mentions “you’ll never believe what’s the answer to challenge 201!” – this suggests problem 201 is special (and is thus likely the problem mentioned in the challenge description that asks what the flag is)
Game: Start
- Click “Start Game”: This takes us to
/game
- Get a problem: The application serves a seemingly random math/logic problem
What happens when we solve a question?
Let’s attempt to solve a problem. The first one I got doing the writeup was: “What is the time complexity of finding an element in a sorted array using binary search?”
Answer: “O(log n)” (this is a common computer science fact)
Result: If correct, we get points and a new problem. If incorrect, we can try again.
The end screen
After solving a few problems, let’s wait out the timer to check out the game end screen to see how our attempts are recorded:
What we discover:
- The game end screen shows all problems we attempted
- For correct answers, it shows the question AND the correct answer
- For incorrect answers, it shows the question but not the answer
- This is crucial information for our exploitation strategy!
Testing the input field
We have a few different options for avenues to explore to exploit this application. We could try guessing some endpoints, we could try to check if server versions are vulnerable, and we can mess with the input fields. When attempting a challenge like this, I often will start off by just putting giberish into every field I can find to try to look for an error. When doing this by putting some symbols like '";:[{]}\|,<.>/?
in the trivia answer box, we get an incredibly revealing response.
Error: unrecognized token: "";:[{]}\|,<.>/?'"
Query: SELECT * FROM problems WHERE id = 180 AND answer = ''";:[{]}\|,<.>/?'
BINGO! We got SQLi!
Let’s try forcing the answer to true
Now let’s try a basic SQL injection that should always return true:
' OR '1'='1
What this should do: Make the query SELECT * FROM problems WHERE id = X AND answer = '' OR '1'='1'
which should always be true because '1'='1'
is always true.
Result: It works! We get the answer marked as true! Is that the end of the challenge, we just try every challenge until we find the one with the flag, enter the payload and then win? Let’s wait until the game end screen to see.
And with doing so, we get a suprising result, the answer for the challenge under “correct answer” is not the correct answer for the challenge… If we try the payload on multiple questions, we’ll see they all share the same “correct answer”, which is certainly wrong. Guess we haven’t solved the challenge quite that easy.
Alex, what is the answer that gets put onto the end screen?
Since simple boolean injection isn’t working as we hoped, let’s try a different approach. Perhaps the query that we’re injecting is somehow returning the values that get returned to the game end screen, this would explain why we always get the same incorrect answer (probably the answer from problem 1). But before we can use UNION SELECT, we need to figure out how many columns are in the query we’re returning from.
Thankfully, We can use UNION statements to figure out the number of columns. UNION combines the results of two SELECT statements, but they must have the same number of columns.
Testing 1 column:
' UNION SELECT 1 --
Result: Error – “SELECTs to the left and right of UNION do not have the same number of result columns”
Testing 2 columns:
' UNION SELECT 1,2 --
Result: Error – “SELECTs to the left and right of UNION do not have the same number of result columns”
Testing 3 columns:
' UNION SELECT 1,2,3 --
Result: Error – “SELECTs to the left and right of UNION do not have the same number of result columns”
Testing 4 columns:
' UNION SELECT 1,2,3,4 --
Result: SUCCESS – We get “Correct answer! New score: 40”
Testing 5 columns:
' UNION SELECT 1,2,3,4,5 --
Result: Error – “SELECTs to the left and right of UNION do not have the same number of result columns”
Conclusion: The problems
table has exactly 4 columns.
Now that we know we have four columns, let’s try injecting random data to see if any of it is reflected in the game end screen.
' UNION SELECT 'a','b','c','d' --
What this does: Injects our own data into the result set, where:
- First column gets ‘a’
- Second column gets ‘b’
- Third column gets ‘c’
- Fourth column gets ‘d’
When we check the game end screen, we see that our injected data appears in the “correct_answer” field. Specifically, we see:
{
"correct_answer": "c",
"problem_id": 183,
"question": "What is the square root of 256?",
"score_earned": 50,
"solved": true
}
What this tells us:
- The third column position in our UNION SELECT contains what gets displayed as the “correct answer”
- We can control what appears in the game end screen
- The application processes our injected data and treats it as a valid problem solution
United we solve the challenge
Now we understand how to extract the flag:
- Problem: We need to get the actual answer from problem 201, not just make the query return true
- Discovery: The third column position in UNION SELECT controls what appears as the “correct answer”
- Strategy: Use UNION SELECT to inject problem 201’s data, with the third column containing the actual answer
Why this approach works:
- Instead of trying to make the query always true, we’re injecting actual data
- The third column position controls what gets displayed as the answer
- If we can get problem 201’s real answer into that third position, it will appear in the game end screen
The final payload:
' UNION SELECT null,null,(SELECT answer FROM problems WHERE id = 201),null --
After using that payload, we simply wait the 30 seconds to reach the end screen, which will show our flag in the correct answer box.