4

So I've been trying to replicate a second order SQL Injection. Here's an example template of two php based sites that I've prepared. Let's just call it a voter registration form. A user can register and then you can check if you're a registered voter or not.

insert.php

<?php

$db_selected = mysql_select_db('canada',$conn);
if (!db_selected)
    die("can't use mysql: ". mysql_error());

$sql_statement = "INSERT into canada (UserID,FirstName,LastName,Age,State,Town)
                    values ('".mysql_real_escape_string($_REQUEST["UserID"])."',
                    '".mysql_real_escape_string($_REQUEST["FirstName"])."',
                    '".mysql_real_escape_string($_REQUEST["LastName"])."',
                    ".intval($_REQUEST["Age"]).",
                    '".mysql_real_escape_string($_REQUEST["State"])."',
                    '".mysql_real_escape_string($_REQUEST["Town"])."')";

echo "You ran the sql query=".$sql_statement."<br/>";
$qry = mysql_query($sql_statement,$conn) || die (mysql_error());
mysql_close($conn);
Echo "Data inserted successfully";
}
?>

select.php

<?php


$db_selected = mysql_select_db('canada', $conn);
if(!db_selected)
    die('Can\'t use mysql:' . mysql_error());
$sql = "SELECT * FROM canada WHERE UserID='".addslashes($_POST["UserID"])."'";
echo "You ran the sql query=".$sql."<br/>";
$result = mysql_query($sql,$conn);
$row=mysql_fetch_row($result);

$sql1 = "SELECT * FROM canada WHERE FirstName = '".$row[1]."'";
echo "The web application ran the sql query internally=" .$sql1. "<br/>";
$result1 = mysql_query($sql1, $conn);
$row1 = mysql_fetch_row($result1);

mysql_close($conn);
echo "<br><b><center>Database Output</center></b><br><br>";

echo "<br>$row1[1] $row1[2] , you are a voter! <br>";

echo "<b>VoterID: $row[0]</b><br>First Name: $row[1]<br>Last Name: $row[2]
    <br>Age: $row[3]<br>Town: $row[4]<br>State: $row[5]<br><hr><br>";
}
?>

So I purposely made this vulnerable to show how second order SQL Injection works, a user can type in a code into the first name section (where I am currently stuck, I've tried many different ways but it seems that I can't get it to do anything). Then when a person wants to activate the code that he has inserted in the first name section, all he needs to do is just type in the userID and the code will be inserted.

For example: I will type into the insert.php page as: userid = 17

firstname = (I need to inject something here)

lastname = ..

age = ..

town = ..

state = ..

Then when I check for my details, and type in 17, the SQL script injected will be activated. Can I get few examples on what sort of vulnerabilities I can show through this?

yoshifish
  • 73
  • 2
  • 2
  • 8
  • Try this demo and if you did not understand or could not apply it, get back to us. http://www.esecforte.com/blog/second-order-sql-injection/ – PyQL Oct 18 '12 at 10:21
  • I based my example from that site. but it didn't work for me either as I couldn't get the output to appear as how it appeared on that example – yoshifish Oct 18 '12 at 10:25

2 Answers2

4

What is there to demonstrate?

Second order SQL injection is nothing more than SQL injection, but the unsafe code isn't the first line.

So, to demonstrate:

1) Create a SQL injection string that would do something unwanted when executed without escaping.

2) Store that string safely in your DB (with escaping).

3) Let some other piece of your code FETCH that string, and use it elsewhere without escaping.

EDIT: Added some examplecode:

A table:

CREATE TABLE tblUsers (
  userId serial PRIMARY KEY,
  firstName TEXT
)

Suppose you have some SAFE code like this, receiving firstname from a form:

$firstname = someEscapeFunction($_POST["firstname"]);

$SQL = "INSERT INTO tblUsers (firstname) VALUES ('{$firstname }');";
someConnection->execute($SQL);

So far, so good, assuming that someEscapeFunction() does a fine job. It isn't possible to inject SQL.

If I would send as a value for firstname the following line, you wouldn't mind:

bla'); DELETE FROM tblUsers; //

Now, suppose somebody on the same system wants to transport firstName from tblUsers to tblWhatever, and does that like this:

$userid = 42;
$SQL = "SELECT firstname FROM tblUsers WHERE (userId={$userid})";
$RS = con->fetchAll($SQL);
$firstName = $RS[0]["firstName"];

And then inserts it into tblWhatever without escaping:

$SQL = "INSERT INTO tblWhatever (firstName) VALUES ('{$firstName}');";

Now, if firstname contains some deletecommand it will still be executed.

Erwin Moller
  • 2,375
  • 14
  • 22
  • thats the problem..I'm not sure how can I store and fetch that code.Single example like storing duplicate entries in the database or anything. something more complex like fetching the database entries with a single click command? – yoshifish Oct 18 '12 at 10:20
  • 1
    I added a more concrete example. – Erwin Moller Oct 18 '12 at 11:01
3

Using a first name of:

' OR 1 OR '

This will produce a where clause in the second SQL of

WHERE FirstName = '' OR 1 OR ''

Therefore the result will be the first record in the table.

By adding a LIMIT clause, you can extract all rows from the table with:

' OR 1 ORDER BY UserID ASC LIMIT 0, 1 --

Obviously it will only extract 1 row at a time, so you would need to repeat that and increment the 0 in the LIMIT. This example uses a comment -- to terminate the remaining SQL which would otherwise cause the query to fail because it would add a single quote after your LIMIT.

The above is a simple example, a more complex attack would be to use a UNION SELECT which would give you access to the entire DB through the use of information_schema.

Also you are using addslashes() in one of your queries. That is not as secure as mysql_real_escape_string() and in turn: escaping quotes with either is not as secure as using prepared statements or parameterised queries for example in PDO or MySQLi.

MrCode
  • 63,975
  • 10
  • 90
  • 112
  • Thanks, well this is just to show the vulnerable side of the website. I have also been trying to delete the table but it doesn't seem to work. How do I add the command to 'drop database/drop table' from the first name section? – yoshifish Oct 18 '12 at 10:42
  • You can't because `mysql_query()` does not support multiple queries so it is not possible to add a DROP query. See: http://php.net/manual/en/function.mysql-query.php – MrCode Oct 18 '12 at 10:46
  • Well because it's a SELECT query, you can only extract data because of unsupported multiple queries, you can't DROP, DELETE, UPDATE or anything else unless the original query was one of those. – MrCode Oct 18 '12 at 10:53
  • Can you provide me an example of how a union select attack could take place @MrCode – yoshifish Oct 18 '12 at 12:25
  • That's more complicated, you need to understand how UNION selects work but here is an example: http://stackoverflow.com/questions/4600954/site-has-been-hacked-via-sql-injection there are also loads of examples on Google – MrCode Oct 18 '12 at 12:48
  • I can't seem to get the LIMIT SQL injection that you described to work. Any help? – yoshifish Oct 19 '12 at 04:32
  • @yoshifish I've edited the answer, try that one. I had a comma that shouldn't have been there, also you need a space after the comment `--` – MrCode Oct 19 '12 at 06:20