 |
Account Login
|
 |
 |
Latest Articles
|
 |
 |
IRC Channel
|
 |
 |
Associates
|
 |
 |
Associates
|
 |
|
 |
 |
|
 |
09-19-2007, 10:32 PM
|
#1 (permalink)
|
|
La Vida es Sueńo
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
|
How to Login to Any Account on an Insecure Site
I sat down with a friend today and spent a good 5 hours attempting to breach the security of an unnamed website. The website in question is a rather popular website with an Alexa ranking of just over 3,000. I've always been of the opinion that esoteric knowledge is only esoteric because the individuals wish to make it that way. Governmental procedures, for instance, are very esoteric. Unless you're actually there, the procedures are well over many individuals' head. If you can decipher the language used most people can understand it.
This is where I'd like to sit down with everyone at TalkPHP and explain in simple terms how I did it, the reasons why and what you can do to prevent this from happening to you.
Note: Although we successfully hacked the target site, no core information was gathered and no harmful information was injected. The administrators of the site were notified and advised on how to patch the vulnerability.
This breach of security involved the common security method, SQL injection. Now, I've used the unnamed site on numerous occasions for various reasons that I'm not going to mention. All perfectly innocuous. However, from using the website off and on I noticed many security issues that were arising from normal use. Today was the day I decided to put theory into practice.
I already had an account and so I attempted to login to my account using the following:
Username: Wildhoney
Password: ' OR 1=1
What that essentially says is take the user name, Wildhoney, and then attempt to issue my own SQL. If you think that the normal query would be like so:
Code:
SELECT
myUsername,
myPassword
FROM
myTable
WHERE
myUsername = 'Wildhoney'
AND
myPassword = 'myPassword'
Then terminating the SQL just after the = ' would end the normal SQL and allow me to enter raw SQL commands. Thanks to our SQL injection the query would look like so:
Code:
SELECT
myUsername,
myPassword
FROM
myTable
WHERE
myUsername = 'Wildhoney'
AND
myPassword = '' OR 1=1
As you can clearly see from there, the SQL has been significantly modified to to make the end part of the SQL say the following in pseudo terms: AND the field myPassword equals NULL OR 1 equals 1. As 1 will always equal 1 we can successfully login.
However, on this website there is more code at the end of the SQL making our MySQL statement now make absolutely no sense. The solution for this is MySQL comments! A comment will comment out any code we do not want. In this case, the code after our OR 1=1. First up was the -- comment block. However, -- only comments single lines and after that didn't work we deduced the site must have been using multiple SQL lines. Step in /*. Once that had been issued MySQL ignored everything after our OR 1=1 and the login was successful.
Note: Although we logged into our own user name, absolutely any user name on the site could have been accessed.
I presume that many individuals are asking "why?". This wasn't a case of boosting our ego or bragging rights. Rather, education. Although we did a lot more after the login attempt, nothing harmless in the least, the login attempt is perhaps 1 of the most vulnerable part to any website and I felt was worth mentioning to everyone on TalkPHP to stop them making the same mistakes in their code.
For the login attempt, the code was not complex nor was it tricky to construct. We successfully logged into our account without specifying the correct password after about the 5th attempt. A little research was required before logging in but after that, the world is yours (Or, ours).
The way to protect yourself against something like that is just so simple. You should escape all single quotes, as well as check the data using a type specifier. See our article on sprintf. You may also wish to check for the encoding type - such as UTF-7 and UTF-8. Character encoding can cause so many issues with MySQL queries. These will certainly be covered in some depth on TalkPHP in the near future.
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
09-19-2007, 10:50 PM
|
#2 (permalink)
|
|
The Frequenter
Join Date: Sep 2007
Posts: 360
Thanks: 24
|
sprintf saves our live!
Last edited by Haris : 09-19-2007 at 11:43 PM.
|
|
|
|
09-20-2007, 12:58 AM
|
#3 (permalink)
|
|
The Contributor
Join Date: Sep 2007
Posts: 31
Thanks: 0
|
Doesn't mysql_real_escape_string() protect against SQL injections?
|
|
|
|
09-20-2007, 01:16 AM
|
#4 (permalink)
|
|
La Vida es Sueńo
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
|
Indeed it can! Fantastic function is mysql_real_escape_string() along with sprintf.
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
09-20-2007, 06:01 AM
|
#5 (permalink)
|
|
The Prestige
Join Date: Sep 2007
Location: Sweden, Stockholm
Posts: 1,053
Thanks: 115
|
PHP Code:
$pass = mysql_escape_string($_POST['pass']); $sql = printf("SELECT `pass` FROM `users` WHERE `pass` = %s", $pass); $query = mysql_query($sql);
That would be pretty foolproof?
Anything else that I can add to make it safer?
|
|
|
|
09-20-2007, 09:46 AM
|
#6 (permalink)
|
|
La Vida es Sueńo
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
|
That's quite safe as it is! Just don't forget to strip_tags() and %s needs to be in single quotes: '%s', else it'll simply fail.
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
09-20-2007, 09:49 AM
|
#7 (permalink)
|
|
Moderateur
Join Date: Apr 2007
Posts: 1,239
Thanks: 3
|
Tanax, in your example the password is sent in a POST request (from a form) and fed (via mysql_escape_string) directly into the query. There are a couple of problems with that SQL query. Firstly, there are no quotes around where the password will go ( ... WHERE `pass` = '%s') -- unless all of your passwords are numbers, that will cause problems. Secondly, you compare the pass column in the database directly to the string sent through the POST request. You should never, ever, store passwords exactly as they are entered into forms (in "plain text"). Wildhoney has written about salting password hashes, as well as one soon to be released on dynamic salting. Finally, your query would be useless in any real terms because it would return a result if any password in the table matched what was POSTed. You must always match it against a parameter unique to each individual (user id number or similar). Oh, and you should use sprintf to "print to a string" rather than the printf that you have there.
All that said, as far as "protecting" the actual value passed from POST that is a good start.
__________________
salathe@php.net
|
|
|
|
09-20-2007, 09:53 AM
|
#8 (permalink)
|
|
Moderateur
Join Date: Apr 2007
Posts: 1,239
Thanks: 3
|
Quote:
Originally Posted by Wildhoney
That's quite safe as it is! Just don't forget to strip_tags() ...
|
In this instance (password verification) why would you want to strip tags? I'm pretty sure that if someone entered the following into the password field: <strong>my<br>pass<br>word</strong>, even if "mypassword" was correct you would still want to fail because the supplied password is incorrect:
"<strong>my<br>pass<br>word</strong>" != "mypassword"
Or am I missing something?
__________________
salathe@php.net
|
|
|
|
09-20-2007, 09:56 AM
|
#9 (permalink)
|
|
La Vida es Sueńo
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
|
No, you're not missing anything. It's just good practice to only allow alphanumeric passwords and so stripping the tags would be a useful exercise.
Here's the article on working with dynamic salts.
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
09-20-2007, 11:49 AM
|
#10 (permalink)
|
|
The Reckoner
Join Date: Sep 2007
Posts: 437
Thanks: 22
|
Unless im completely missing something here, why has no one mentioned the fact that selecting a users password by password is prone to problems. For example, if two users have the same password the user who registered first will always be returned. If the query is geing used to validate a login the query is very insecure indeed. You'll need to also include the user's id, username, email or other primary key and use that to determine if the password belongs to the user.
|
|
|
|
09-20-2007, 12:05 PM
|
#11 (permalink)
|
|
Moderateur
Join Date: Apr 2007
Posts: 1,239
Thanks: 3
|
I mentioned it Karl, but it's good to have the message said twice because it's important. :)
__________________
salathe@php.net
|
|
|
|
09-20-2007, 12:23 PM
|
#12 (permalink)
|
|
La Vida es Sueńo
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
|
I just constructed the following which I think would be pretty safe. It's a different way of doing the login and would throw would be hackers off the trail. If you're expecting a 32 bit string and nothing more then the substr is a good idea. The advantages of putting the if statement in the select area is that commenting the rest out would make the query fail, it also does it differently to how most people would do it so the hacker may be barking up the wrong tree. However, the disadvantage I see of putting it there is that you can modify the query more if your SQL is still insecure.
Don't get me wrong, I'm not at all adverse to the normal way of doing it as that's no doubt the best way due to the fact that you can't UNION a DELETE onto the end.
Code:
SELECT
@myPass:= SUBSTR(MD5('myPassword'), -32),
IF(password = @myPass, TRUE, FALSE) AS status
FROM
members
WHERE
username = 'myUsername'
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
09-20-2007, 12:31 PM
|
#13 (permalink)
|
|
The Reckoner
Join Date: Sep 2007
Posts: 437
Thanks: 22
|
Is there really any advantage to that over the tradional method? Surrounding passwords in quotations and ensuring you escape the data should be sufficient to stop the majority of attackers. I dont see any benefit to using the alternative method over the standard method.
|
|
|
|
11-13-2007, 09:52 AM
|
#14 (permalink)
|
|
The Acquainted
Join Date: Nov 2007
Location: Sweden
Posts: 100
Thanks: 13
|
My standard login mechanics - is it secure ?
While reading this interesting thread i got a little worried that my standard login code had short commings. so i wish for you to take a look and see if i can make some improvements to it.
I apologize in advanced for my english since i´m from sweden and it might be a little rusty. :)
PHP Code:
$user = strip_tags(trim($_POST['adm_login_user']));
$password = strip_tags(trim($_POST['adm_login_psw']));
// login --------------------------------
$sql = "SELECT * from com_usr WHERE is_username='" . $user . "' AND is_password='" . md5($password) . "' AND active=1";
$result = mysql_query($sql);
if (!$result) {
echo("Could not perform MySQL query: " . mysql_error() . "");
exit();
}
$user_exist = mysql_num_rows($result);
while ( $row = mysql_fetch_array($result) ) {
$id = $row["ID"];
$username = $row["is_username"];
$active = $row["is_active"];
$admin_lvl = $row["is_admin_level"];
$auth = $row["is_author"];
}
if ($user_exist > 0) {
// Do something like setting session variables
} else {
// Send user back with error message
}
|
|
|
|
11-13-2007, 11:35 AM
|
#15 (permalink)
|
|
La Vida es Sueńo
Join Date: Sep 2007
Location: Oldham
Posts: 2,215
Thanks: 90
|
Well, assuming that GPC magic quotes is disabled - which they will be in the next version of PHP as it is a real annoyance, your login script is quite easy to crack, I'm afraid. With them removing magic quotes all together in PHP6, people really need to get up-to-scratch with the way they do things.
I can by crack your login script by entering a user name like so: 'bleh' /* All this does is ends the user name segment and then comments out the rest of the code.
You may wish to have yourself a read through this article and also use a function, such as the one I used below in a few of my projects:
And thus when used in conjunction with the sprintf function. Your MySQL will now look something like this:
php Code:
$sql = sprintf(" SELECT * from com_usr WHERE is_username = %s AND is_password md5(%s) AND active = 1", mysql_parse_value ($user), mysql_parse_value ($password));
This would make my earlier attempt futile, and in all honesty will just make me end up sat there looking silly. You'd have got one over on me :) ! Moreover, as you're new to TalkPHP, we prefix all our variables by their data-type - this admittedly may seem somewhat confusing to begin with, but please have a read through Bluesaga's article and you'll soon understand. It makes code look a thousand times better.
Last but not least, you're in safe hands with your PHP code now :). Glad to have you here.
__________________
The man who comes back through the Door in the Wall will never be quite the same as the man who went out.
|
|
|
11-13-2007, 12:36 PM
|
#16 (permalink)
|
|
The Acquainted
Join Date: Nov 2007
Location: Sweden
Posts: 100
Thanks: 13
|
Thank you Wildhoney for the quick reply.
I´m going to go over your post and sugestions more in detail when i get of work.
And i´m going to read the Bluesaga article so that i use the proper variable naming convention.
/EyeDentify :D
__________________
Of course the whole point of a doomsday machine, would have been lost if you keep it a secret.
|
|
|
|
12-14-2008, 08:30 PM
|
#17 (permalink)
|
|
The Visitor
Join Date: Dec 2008
Location: Tehran, Iran
Posts: 2
Thanks: 2
|
I created my personal! function:
PHP Code:
function CleanInput($input) {
return trim(str_replace(array("/","'",'"',";",":","+","--","*"), "", $input));
}
and what I do in login pages is :
- First i check that the user exists or not
- Then i retrive user information
- Passwords which are entered in form & are in DB (both in md5) compared with php
- Session/cookie sets with entered information
and in pages that need user login status, same proccess are done (except 4)!
Is it correct ?
(<!-- english problem -->) 
|
|
|
|
12-14-2008, 08:46 PM
|
#18 (permalink)
|
|
Wizard
Join Date: Sep 2007
Posts: 1,216
Thanks: 17
|
First off, this thread is over a year old. It belongs in a new thread, please read the dates before posting.
Second, that technique will not work and it strips functionality.
To inject anything, you just have to separate it by a removed character, this:
Will return
Thus opening it to attack.
It also takes out characters that could be used for legit purposes. Escaping them is how it should be done and mysql_real_escape_string() does this just fine (I see no real use for sprintf, it seems like adding another layer of processes with no advantage).
|
|
|
|
|
The Following User Says Thank You to Village Idiot For This Useful Post:
|
|
12-14-2008, 08:58 PM
|
#19 (permalink)
|
|
The Visitor
Join Date: Dec 2008
Location: Tehran, Iran
Posts: 2
Thanks: 2
|
Quote:
Originally Posted by Village Idiot
First off, this thread is over a year old. It belongs in a new thread, please read the dates before posting.
|
Oh, I didn't see the year !
Quote:
Originally Posted by Village Idiot
Second, that technique will not work and it strips functionality.
To inject anything, you just have to separate it by a removed character, this:
Will return
Thus opening it to attack.
It also takes out characters that could be used for legit purposes. Escaping them is how it should be done and mysql_real_escape_string() does this just fine (I see no real use for sprintf, it seems like adding another layer of processes with no advantage).
|
Hmmm, it seems you are right
|
|
|
|
|
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
|
|
|
| Thread Tools |
Search this Thread |
|
|
|
| Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
|