3

I'm studying infosec, starting to make my way through some of the online wargames out there. Have recently been working on Natas by OverTheWire (http://overthewire.org/wargames/natas/) and it's been great fun so far. My question is related to one of the challenges in the game.

** SPOILERS FOR THE GAME FOLLOW **

On one of the levels, I am given my first encounter with mysql. The backend code looks like this (the game lets you see the php code for each function, at least at the start):

<?
if(array_key_exists("username", $_REQUEST)) {
$link = mysql_connect('localhost', 'natas14', '<censored>');
mysql_select_db('natas14', $link);

$query = "SELECT * from users where username=\"".$_REQUEST["username"]."\" and password=\"".$_REQUEST["password"]."\"";
if(array_key_exists("debug", $_GET)) {
    echo "Executing query: $query<br>";
}

if(mysql_num_rows(mysql_query($query, $link)) > 0) {
        echo "Successful login! The password for natas15 is <censored><br>";
} else {
        echo "Access denied!<br>";
}

mysql_close($link);
} else {
?>

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password"><br>
<input type="submit" value="Login" />
</form>
<? } ?> 

I began researching sql injection, and tried some basic things like foo OR 1=1 as input, but got nothing. I looked at the code and tried inserting quotes with my input, which would throw me errors when I had an odd number of quotes in the input, but didn't know where to go from there.

I eventually started working through the OWASP page about testing for sql injection, and during testing their examples, I came across something which said to try:

$username = 1' or '1' = '1
$password = 1' or '1' = '1

That didn't work, but for some reason, my intuition said to change the first ' to " in the $username input, and the last ' to " in the $password input. So it became

$username = 1" or '1' = '1 
$password = 1' or '1' = "1`

And... it worked. satisfied the condition for this block

if(mysql_num_rows(mysql_query($query, $link)) > 0) {
    echo "Successful login! The password for natas15 is <censored><br>";
} else {
    echo "Access denied!<br>";
}

and gave me the password for the next level.

My question is... WHY? I don't know enough about sql injection as I'm obviously a noob, but I don't get what exactly is happening on the sql server with that particular input.

To reiterate, the query looks like this (I used the debug feature in the code above to get it to print the query):

Executing query: SELECT * from users where username="1" or '1' = '1" and password="1' or '1' = "1"`

What is the behavior of this query, and how is sql evaluating it to give me a TRUE output on both inputs (at least I assume that's what it's doing...)? Whenever I change the " to ' in any other configuration within the query, it fails. And the fact that there are an odd number of " and ' within the query is driving me crazy.

Any advice or explanations would be greatly appreciated. I feel like I won't have truly passed this level until I understand what is happening!

4

Whenever I change the " to ' in any other configuration within the query, it fails.

That would be:

SELECT * from users where
    username = "1' or '1' = '1"
and
    password = "1' or '1' = '1"

This fails because you're literally querying for a user called 1' or '1' = '1 with a password 1' or '1' = '1. If you want to escape from double quotes, you need to use double quotes yourself - as you did. This is your successful query reformatted:

SELECT * from users where
    username = "1"
or
    '1' = '1" and password="1'
or
    '1' = "1"

Since '1' == "1", this evaluates to true.

And the fact that there are an odd number of " and ' within the query is driving me crazy.

There isn't actually an imbalance of quotes. In particular, '1" and password="1' is one single-quoted string which just happens to have spaces and double quotes inside.

  • This is it! Thank you. I should have just done more research on query syntax! – winter Feb 5 '18 at 2:46
1

In your query:

SELECT * from users where username="1" or '1' = '1" and password="1' or '1' = "1"

username="1" evaluates to false

'1' = '1" and password="1' evaluates to false, as don't get confused by double quotes, these are treated as normal string because they are inside single quotes

'1' = "1" evaluates to true, because you can use either single quotes and double quotes, and on both sides you have same string "1"

Since you have OR between these statements, it's enough for only one of them to be evaluated to true, to actually match all rows. This way, the query pulls all rows from table, because 1=1 is true for all rows, and script evaluates the 1st row, which is actually what is interesting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.