I am trying to inject into a dummy website I have made, its a simple form which uses the text input to send data to my php file and then outputs the data gathered. The following is my code for the SQL.
$id = $_GET['id'];
$id = $_GET['id'];
$data = $conn->query('SELECT * FROM users WHERE username = ' . $conn->quote($id));
foreach($data as $row) {
echo $row['id'].' '.$row['username'];
}
When I try to use things such as unions I get no data back and if I put an apostrophe at the end of the URL I don't get a MySQL error. Could someone please explain why the site is secure from SQL injections?
As there are some confusions as to what I asked my final goal is to be able to get into the information schema so I have been trying to use statements like to get into the schema but without success:
' and 1=1 union select table_name,table_schema from information_schema.tables where table_schema='users' #
Other apparently may have missed what you were asking...
You are INTENTIONALLY trying to sql-inject your own site, such as for personal learning on how NOT to, but also see what impacts sql-injection CAN do. If so, take a look at your statement and see "what would I need to add to fake it out".
"SELECT * from users WHERE username = '$id'"
If the user puts a value such as "Bill" for the $id, it would become
"SELECT * from users WHERE username = 'Bill'"
and run no problem. Now, you want to inject and see ALL users, a common way is to close the quote and then add something else that will always return true... such as a user puts a value of
' OR 1=1 ;--
The above would result in
"SELECT * from users WHERE username = '' OR 1=1;-- '"
The semi-colon and dashes are important to "finish" the original query, and then indicate that anything after the dashes are comments so it won't try to execute anything AFTER the otherwise dangling close quote from your original query build construct.
Hopefully that helps answer why you may be failing while TRYING to inject into your own site.
COMMENT FEEDBACK
I don't know why my version would not work, I am not trying to union anything, just force an all records returned.
With respect to your UNION clause, that looks ok, but if your users table has 3 columns and your UNION is only 2 columns, that should fail as the union should be the same number of columns as in the original query. THAT would cause a failure on execution, but not enough specific information to confirm.
Most likely, you have magic quotes enabled, which is saving your otherwise-vulnerable code.
Don't rely on it.
As it can be clearly seen from either question and answers, most people don't understand what injection is. For some strange reason everyone takes injection consequences for injection itself. While injection is just a query creation. No more, no less.
So, the result of injection is not whatever data returned, but mere SQL query string. Thus, what the OP have to check is the resulting SQL query. It is extremely simple a task, as primitive as just echoing the query string out. This will reveal injection possibility immediately, without toilsome guesswork and sophisticated query building.
Simple output like this
SELECT * from users WHERE username = 'Bill\''
will tell you that magic quotes are on and whole question is a thousand-times-duplicate and not-a-real-one at once.
UPDATE
For some strange reason the code in the question mysteriously has been changed to invulnerable PDO based code. Which leads me to believe that whole performance were just a mere trolling.
Your injected SQL String should look like this
-1'/**/UNION/**/SELECT/**/1,##VERSION/**/FROM/**/users/**/WHERE/**/1='1
as you need to close the last ' in the final sql query
Update:
like Your Common Sense pointed out
For some strange reason the code in the question mysteriously has been changed to invulnerable PDO based code. Which leads me to believe that whole performance were just a mere trolling.
Related
Is prepared statement necessary when dealing with trusted data fetched from another query?
For example.
When a user is navigating throughout the site, they click named links like this: /?category=health where health is the value that is sent to the database.
In this scenario I of course use prepared statement like this:
$qry = $dbh->prepare('SELECT category_id, and, other, columns FROM categories WHERE query_value = ?');
$qry->execute([$_GET['category']]);
$get = $qry->fetch();
$qry = null;
But further down the script, I would display content associated with the selected category based on the categories.category_id fetched from the last query.
$Banners = $dbh->query('SELECT image FROM Banners WHERE category_id = '.$get['category_id'])->fetchAll();
I would like to think that this is a secure query.
That the value could be no other than a trusted value since it has to be a result from the previous query?
And this query won't be executed if the previous query doesn't return true.
Here's how I've done it so far:
It's a 3-liner. But it would speed up the coding part a bit if I was certain that the 1-liner above is fine too.
$qry = $dbh->prepare('SELECT image FROM Banners WHERE category_id = ?');
$qry->execute([$get['category_id']]);
$Banners = $qry->fetchAll();
What this all is about largely is about ensuring your SQL syntax is sane and what you think it is. Preventing malicious attacks is just a corollary of that. What if your category ids in the future spawn apostrophes or whatnot as part of the regular value? You need escaping for correct syntax then anyway, or, better, prepared statements.
Secondarily, there is second order injection, in which a value which was previously treated as unsafe is now suddenly treated as safe, even though it still has the potential for attacks. E.g. if your values legitimately contain apostrophes.
More broadly speaking: how do you ensure a value is "safe"?
// $get['category_id'] is safe and doesn't need escaping
'SELECT image FROM Banners WHERE category_id = '.$get['category_id']
Well, how do you know this? What code ensures this value is safe? Where does this value come from? Are you sure that's where it comes from? You're outsourcing the integrity of this query to some other part of the code here. How can you be sure that other part does its job correctly? Now, or in the future, after a lot of refactoring? Simply ensure yourself that you are producing correct queries by writing queries in a secure way, don't outsource your security.
You need to ask yourself: is this query prone to injection the way it is written? And the answer to the above example is a plain yes, it is prone to injection, and the safety merely depends on your trust that $get['category_id'] is what you think it is.
I was running a site I purchased that I thought was fairly unhackable. However, after having an attack, I found it was not. He informed me of the vulnerability, however my question is what user input could have been done to get all the users usernames like he did? Here is the code...
$un=$_GET['username'];
$q=$db->query("SELECT * FROM users WHERE login_name='$un' OR username='$un'");
I realize that this is highley hackable. Therefore, I changed the site over to prepared statements to prevent this from happening again. I just want to know what he could have entered to get all the users usernames.
Someone posted the script on github, you can find it here:
https://github.com/sat312/Mafia-Game-Script/blob/master/checkun.php
' OR 1=1;
In the URL:
/yourScript.php?username=%27%20OR%201%3D1%3B
The idea is that since data is mixed with the command, you can just finish the command with data.
You get $un from the user, so I can type anything I want and it'll get substituted into your query. It's called a SQL Injection attack.
Lets say $un = ' OR 1 = 1;-- then your query becomes:
SELECT * FROM users WHERE login_name='' OR 1 = 1;--' OR username='' OR 1 = 1;--'
What will happen? this gets executed:
SELECT * FROM users WHERE login_name='' OR 1 = 1;
This will return every row in the table.
He may have used the GROUP_CONCAT statement in MySql which basically groups a column in multiple rows into a single row (see Can I concatenate multiple MySQL rows into one field? for more information). He may have terminated the original SQL statement or UNIONED it with his own and added a LIMIT and ORDER BY to ensure his result got returned and commented out the remained of the original statement.
This is one possibility, but there are probably a few others.
I have this code:
<?php
$table = $_GET ["table"];
$query = "SELECT 1 FROM $table";
$st = $pdo->prepare($query);
$st->execute();
This is not the real code, but it is an example to get the idea.
If I make:
hacked.php?table=users;DROP TABLE users;
It will work, cause it is not correctly escaped.
However, if I want to update information like this:
hacked.php?table=users; UPDATE users SET name="abc" WHERE name="def";
It will not work, cause since it is escaped, pdo will convert the query to
SELECT 1 FROM users; UPDATE users SET name=\"abc\" WHERE name=\"def\";
and obviously it fails.
Is there anyway to make this query works?
EDIT 1
We have one guy in our team only devoted to check my code and hacked it. So I want to be ready if this can be in some way accomplished.
EDIT 2
I was already read this: Are PDO prepared statements sufficient to prevent SQL injection? but it really did not answered my question. However it gave me a way to go through. And the solution of #duskwuff was the same I came to. So, for the admins, if this should be removed or marked as a duplicate is ok. But I insist that this can be helpful for someone to know how pdo prepared can be hacked.
It will not work, cause since it is escaped, pdo will convert the query to
SELECT 1 FROM users; UPDATE users SET name=\"abc\" WHERE name=\"def\";
This is incorrect! PDO does not perform escaping on text that is interpolated into queries, as it has no awareness of what text was interpolated. What you're seeing is the result of PHP's deprecated magic_quotes feature adding backslashes to the content of request variables (like $_GET and $_POST). Even if this is enabled, it can be trivially avoided in a query like this one by using non-quoted constructs such as:
SELECT 1 FROM users; UPDATE users SET name = CHAR(97,98,99) WHERE name = CHAR(100,101,102)
(CHAR() is a MySQL function which constructs a string from a list of character code values. If you're using some other database, an equivalent function probably exists.)
Interpolating unescaped content directly into a query is never safe. Don't do it.
I think you are asking the wrong question. If you have code that is even remotely similar to this, then you have a huge problem with the way you're writing code... and probably with the way you're conceptualizing the problem that you need to solve, or you're working from a very bad design.
If, for some reason, you have a need for anything about the design of your database to be passed in on a URL query string or an http post, and if, for some reason, you think executing an unescaped query is the approach you need... then whatever you're doing, you're doing it wrong.
If, by some remote chance, you actually have a need to pass the name of a table to a web page, then the very least you must do is compare the input value to some kind of static structure to see if the input value is in the list... and then use the value from the list, or from something static, never from the input.
Simplistically something as primitive as the following would be a far superior approach, though arguably it is a bad design if table names, column names, or any database internals ever need to go out into browser-land.
$table = $_GET ["table"];
IF ($table == "users")
{
$query = "SELECT 1 FROM users;"
}
ELSEIF ($table == "points")
{
$query = "SELECT 1 FROM points;"
}
...
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
What is SQL injection?
I see a lot of php code floating around on stackoverflow and (too) little escaping of strings.
Can anyone
Explain what SQL injection is;
Explain what it can do to your server, data and code;
Give an example how to perform an SQL-injection
Give php sample code how to protect against SQL-injection
An SQL injection is a maliciously formed SQL query used to "confuse" an SQL database into giving something it shouldn't. For instance, consider the following query
"SELECT * FROM `users` WHERE `username` = '$name'";
In a normal case, this will work. If we submit 'Jack' to this, it will return all users named Jack. However, if a user enters, say "' OR 1=1", the resulting query would be
"SELECT * FROM `users` WHERE `username` = '' OR 1=1";
Since 1 always equals 1, and the combinating clause is OR, this will return true on every row, which will in turn display EVERY row to the malicious user. Using this technique, someone can view your entire database. Also consider if someone submits something like "'; DROP TABLE users";--, which results in
"SELECT * FROM `users` WHERE `username` = ''; DROP TABLE `users`";--";
Which is two queries, one which will do nothing, the second which will delete the ENTIRE users database, resulting in the loss of your data.
The best method to prevent SQL injections is to use prepared statements. With these, you send a query to the SQL database that says something like
"SELECT * FROM `users` WHERE `username` = '?'";
This lets the database know the format of the query (WHERE username equals some value), so there is no confusion when given a plain text query. Then the database knows to expect one value, and where to put it. Then you pass that value to the database which it can use to search. This is also better as the database can optimize the query for faster searching.
Read up on prepared statements, which will explain this in more detail.
I cannot resist aswell.
SQL Injection is "a code injection technique that exploits a security vulnerability occurring in the database layer of an application". In other words it's SQL code injected in as user input inside a query.
SQL Injections can manipulate data (delete, update, add ecc...) and corrupt or delete tables of the database. I'm not aware of SQL Injections manipulating scripts though.
Let's say in your PHP script you are expecting (as user input) a username and a password from the login form that are later used inside a query such as:
SELECT Id FROM Users WHERE Name = $name AND Password = $password;
The user can insert inside $name and as $password whatever he likes (for example trough an <input>). Let's imagine he adds a name such as "1 OR 1 = 1; --", the query will now look like:
SELECT Id FROM Users WHERE Name = 1 OR 1 = 1; -- AND Password = $password;
and then, after the ; I could add another query or make the script think that the username and the password actually exists.
Notice that -- AND Password = $password; is a SQL comment and will therefore be ignored.
If you are using PHP < 5 then you should look for mysql_real_escape_string() and use it to escape user inputs before embedding it inside a query.
If you are using PHP5+ you should use PDO or the mysqli extension which can prevent this problem via prepared statements.
I cannot resist posting this.
1- Sql Injection is explained better in one cartoon, than most other documents.
2- Mostly it does not do much to the server, but only to the underlying data. Consequence include delete, insert , select records, drop, create tables. (based on permissions etc..)
3- Examples.
4- Sorry I do not know PHP. But as long as you can abstract your DB layer from your View, you should be fine.
There's a lot of information out there (and elsewhere in here) about this subject, so do not take this answer as a complete list by any means and continue to research on your own...
Explain what SQL injection is;
Explain what it can do to your server, data and code;
Give an example how to perform an SQL-injection
Give php sample code how to protect against SQL-injection
SQL injection is where an attacker discovers that an input value supplied to your application is being sent directly to a database and realizes that they can craft that input to be a custom SQL command. It could be something as simple as entering a special character (such as %) into a text field and receiving a strange response.
It can do anything your database allows that command to do. For example, if your web application has DB owner permissions for the application's database then an attack can potentially drop tables or even drop the whole database. Or, with even normal application permissions, the attack can over-write data or read sensitive data (such as plain text passwords if you have those).
For example, if an application has a text field where you enter a username. If that field is open to SQL injection, an attacker can enter something like: MyName';DROP TABLE Users;-- In this example, the attack manually finishes the query with the closing single quote and semi-colon, then adds another query, then comments out anything afterward. If not protected against this, the database may run both queries.
This one I don't know updated enough information, but there's lots out there :)
I have a php file which at the start, assigns some variables from what was sent using $_GET.
It then does some mysql queries, processes the output, then echos out some text and variables.
The only protection I have set in the code is mysql_real_escape_string on the GETs.
Is that enough to prevent attacks?
What else can be done?
Well, you take mysql_real_escape_string awfully wrong.
It's not your fault though - its one of wickedest delusions among PHP society. Even official man page put it all wrong.
This function has nothing to do with securing anything in general and GET variables in particular
This function is merely escaping string delimiters, to make string delimiters unable to break a string. Thus, 2 most important consequences:
not only GET variables but ALL variables being put into query in quotes should be processed with mysql_real_escape_string(), no matter of their source or origin or possible dangerousness
it will have effect on the quoted strings only. It's totally useless to use this function for any other part of query, LIMIT clause variables for example.
Thus, to secure your SQL query, you have to follow whole set of rules, not just deceiving "sanitize your data with mysql_real_escape_string".
You can learn how to protect your SQL from my earlier answer on the similar topic: In PHP when submitting strings to the database should I take care of illegal characters using htmlspecialchars() or use a regular expression?
update
a scenario to show why mysql_real_escape_string is not a silver bullet
being given with url
http://www.example.com/news.php?offset=99999+UNION+SELECT+password+FROM+users+--
a code
$offset = mysql_real_escape_string($_GET['offset']);
$sql = "SELECT title FROM news LIMIT $offset,20";
Will result if not in not so pompous as little bobby tables' one but in somewhat no less disastrous.
No, there are plenty of attacks that you might not have protection for. One example is CSRF. It's a big field, so I recommend reading up on this stuff on the Owasp site:
http://www.owasp.org/
Using this is definitely not sufficient. It is not even sufficient when you only consider sql injection. It is sufficient when you consider sql injection on strings only, but as soon as you have an integer (say an id) it goes wrong:
http://example.com/foo.php?id=10
Goes through:
$q = "SELECT * FROM foo where id = " + mysql_real_escape_string($_GET['id'])
Which results in de SQL query:
SELECT * FROM foo where id = 10
This is easily exploitable, for instance:
http://example.com/foo.php?id=10%3B%20DROP%20TABLE%20foo
Goes through:
$q = "SELECT * FROM foo where id = " + mysql_real_escape_string($_GET['id'])
Which results in de SQL query:
SELECT * FROM foo where id = 10;DROP TABLE foo
I hope this clarifies why it isn't enough.
How you should solve this? Define what input is allowed, and check that the input is indeed of that form, for instance:
if(preg.match("^[0-9]+$",$_GET['id']){
// your code here
}else{
// invalid id, throw error
}
But the best way to be on the safe side (regarding SQL Injection) is using prepared statements:
http://php.net/manual/en/pdo.prepared-statements.php
mysql_real_escape_string will only protect you agains SQL Injections when you use the return value in a MySQL string declaration like:
'SELECT foo FROM bar WHERE quux="'.mysql_real_escape_string($val).'"'
It won’t protect you if you use it in any other context (specifying the sorting order with ASC/DESC, row limits, table/row names, etc.). In that case you’ll need to validate/filter/sanitize the value separately.
And if the user data can also be part of the query result that you are about to output, use htmlspecialchars to replace any HTML special character.
you have if the get variables have values using the isset() and empty() functions