Thursday, November 1, 2012

OvertheWire - Natas Wargame Level 14 Writeup

Level 14

Using the credentials obtained in the previous post, we can login to Level 14 where we are presented with the following screen:

We see that to pass this level, we will need to find the correct Username and Password (or will we?). Let's take a look at the sourcecode:

 <head><link rel="stylesheet" type="text/css" href=""></head>   
 <div id="content">   
 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>";   
 } else {   
 <form action="index.php" method="POST">   
 Username: <input name="username"><br>   
 Password: <input name="password"><br>   
 <input type="submit" value="Login" />   
 <? } ?>   
 <div id="viewsource"><a href="index-source.html">View sourcecode</a></div>   

We can see that this code creates a connection to a MySQL database on the localhost, selects the 'natas14' database, then constructs and executes a query. A key thing to notice is that if we provide a 'debug' parameter in our GET request, it will echo back the exact query being sent to the database. Let's use this and provide a username and password to see the query being sent:

Username: test_username
Password: test_password


Visiting this URL, we receive the following response:

We can see that our provided username and password are embedded within double quotes. If we were to just try and brute force these values, this would take a long time. What if we could somehow embed our own SQL code into this statement? If we could do that, then we could look into options that would allow us to bypass these checks. But more on that in a bit, let's first see if we can break out of these quotes by including a double quote in our username field:

Username: test"username
Password: test_password


We receive the following response when we visit the URL:

Awesome. Not only can we see that our double quote isn't sanitized and instead is included directly in the command, but we can see that we receive a mysql_num_rows() error, which tells us there was an error in our MySQL command, which caused it to not return any rows. We can deduce that the error occurred because our added double quote cause the quotes to not line up correctly. Now we have the condition username="test" followed by an incorrect username" that does not make sense to MySQL.

So we can execute our own SQL code by escaping from these double quotes, but now what can we do with it? A common technique is to give MySQL a tautology, or "always true" statement, that will allow us to bypass the username and password checks. An example looks like the following:

SELECT * FROM users WHERE username="test" OR "1"="1" AND password="test" OR "1"="1"

The content we provide is in orange. This causes the database to check and see if there exists a record where the username is "test" or if the string "1" = "1", which is always true.

The key thing to note here is that we have to make our quotes match up. Therefore we need to refrain from closing the quote on our final string, and let the PHP query do it for us. Let's run this query and see what happens:

Username: test" OR "1"="1
Password: test" OR "1"="1

Using this query, we receive the following result:

Success! We were able to bypass the authentication check and cause the system to log us in as the first user in the users table, which in this case is natas15. We can use this password to log in to the next level.

More writeups to come.


No comments:

Post a Comment