BoneIdol

News Main
BoneIdol

Age/Gender: 22, Male
Location: S. Yorkshire, England

Newgrounds Stats

Sign-Up Date:
8/14/06

Level: 5
Aura: Neutral

Rank: Civilian
Blams: 0
Saves: 0
Rank #: 0

Whistle Status: Normal

Exp. Points: 250 / 280
Exp. Rank #: 135,707
Voting Pow.: 4.41 votes

BBS Posts: 819 (0.68 per day)
Flash Reviews: 0
Music Reviews: 0
Trophies: 0
Stickers: 0

Somebody in the forums made a very vague hint about my newgrounds news/blog thing, which has reminded me this thing exists (he also got my name wrong, but he appologised profusely afterwards). So anyway, this thing is rather spartan and could use a few more interesting posts for all you PHP junkies. So I'm going to write about every hacking technique I know and how to stop neerdowells like myself from getting an easy ride. This is a work in progress, by the way.

I guess I'll start with the biggest exploit to look out for; XSS (Cross-Site Scripting). Oddly, no one ever seems to write much about this handy little exploit. What is XSS, you ask? Simply put, it's where I inject html or javascript code into your website that does bad things like steal peoples' cookies, spam popups, redirect them to another site and even exploit browser vulnerabilities to hack people by proxy.

There are three kinds of XSS; dom-based, persistent and non-persistent. With exception of persistent XSS, they tend to rely on social engineering to some degree; whereby someone has to be convinced to open a boobie-trapped link or upload a boobie trapped file.

DOM (Document Object Model) based XSS

The main thing that makes XSS DOM based is that the vulnerability is in a local file. Any javascript that takes user input from a url variable can be exploited by this. The whole point of this is to try and execute javascript with local file permissions. This doesn't get used that often really. Just avoid using url variables with javascript and/or saving html files to your system.

Non-persistent XSS

Non-persistent XSS involves injecting into an actual html document request before it gets sent to the user, allowing you to bypass the browser sandbox and get your code working in another domain. This stems from you outputting unfiltered user input from a $_GET variable. You can send someone a boobie trapped link to steal their cookies, for example, which I could then use to trick a website into thinking I was logged on as you.

How to sort this? Liberal htmlentities() and typecasting will easily do the job.

Persistent XSS

This is the worst example; pretty much one of the other two except that the exploit gets stored on a file on the site or in a database somewhere. All someone needs to do is visit a page that accesses this file or database row and they're screwed. Since this can affect any visitor rather than a specific target or someone unlucky enough to click a bad link, these should be your top priority.

Like non-pesistent XSS, htmlentities() and typecasting will sort this. Just make sure to use them as you write user input to file or insert them into a database.

If someone needs to edit a post later, html will see the htmlentity codes as the original characters, so you can store them in an encoded state and forget about them. Should you need the original post in a plaintext environment there is the handy htmlentities_decode function in php to help you out.

Ok so that covers XSS, now onto the next big one; SQL injection. This one gets loads of coverage and is seen as something of a boogie man for website developers. In all honesty it is actually very hard to successfully exploit an SQL injection vulnerability, but by god are you going to have a terrible time when someone does.

SQL Injection

SQL injection is basically where someone types some SQL code into user input that gets used in a mysql_query() function (or your framework's equivelent). Say you have the PHP:


$user = mysql_query( "SELECT * FROM users WHERE username = '$_POST[username]' AND password = '$_POST[password]' LIMIT 1;" );

I could just put admin';-- into the username text input. Guess what that -- is? It's a comment in SQL. Now everything past username is ignored and we get a free login as the admin. Pretty nasty vulnerability as you can see.

However the main strawman people throw up is this:


admin'; DROP TABLE users; --

Which, let's face it, wouldn't be fun. These people, however, are talking out of their bottom if they're using this example with PHP. On the sixth line of the mysql_query documentation it says "multiple queries not supported". mysql_query will pretty much ignore that second DROP TABLE query. Your only worry with PHP is in people injecting extra attributes and commenting out code.

Only that's not strictly true, at least not if you use the mysqli or pdo components to run queries. These support up to date mysql 5 features which include sub queries. While the above example would fail miserably, we could probably re-write it to something like:


admin', id = (DROP TABLE users); --

So how do we stop these nasty SQL injections? If you're using plain old mysql or mysqli functions, then typecasting and liberal use of the mysql_escape_string() function is the way to go. This function will automatically add a backslash (\) in front of any dangerous characters like " or '. Just wrap any user input in mysql_escape_string() and typecast it to be the correct variable type.

The only thing to look out for is if your host has safe_mode on. This will automatically do this with " and 's before the user input even gets to your php script. The downside of safe mode is that the backslash gets escaped again as well as the ", so you end up with phantom backslashes appearing in your database. You can remove all these with a quick pass through strip_slashes().

If you use PDO, ignore this advice. PDO supports prepared queries. These lovely little buggers pretty much eliminate all SQL Injection vulnerabilities if used properly. I can't say I've used PDO very much at all (since I discovered CodeIgniter) but a prepared query would look like this:


$pdo = new PDO( 'mysql:host=localhost;dbname=example', 'root', 'password' );
$pdo->prepare( 'SELECT * FROM users WHERE username = ? AND password = ?;' );
$pdo->execute( array( $_POST['username'], $_POST['password']) );
$users = $pdo->fetchAll();

When you execute the prepared statement, you replace the ?s with the contents of the array passed to execute. These values are automatically escaped so at worst your query will fail. Anyway, I'm getting bogged down on PDO, if you want to learn more about it look here.

File exploits

Now I'm going to cover file uploads. File uploads are user input (people forget this) and subject to both XSS and SQL injections (in fact both my examples above are valid file names), but they also have their own set of vulnerabilities to look out for.

The main thing you need to watch out for in files is url encoded file names. Chances are you've seen a file with a load of %20s in its name. %20 is the url entity for space. You can very easily pass a file name characters that would be illegal when decoded. For example ../index.php is an invalid filename. ..%2Findex.php isn't, but php automatically decodes url codes AND file upload functions will overwrite files without asking.

Nice image I painted for you there huh? So how do we prevent this? We simply pass the filename to the basename() function. This function returns the filename from a filepath. So ../index.php becomes index.php. PHP.net actually uses basename in it's examples but never explains why, so lots of people don't bother to use this.

Another special note should be given to include and require functions. Do not, under any circumstances, use these functions to include a user uploaded file into a php script. Include and require will execute PHP code regardless of whether the file is a .php file or not.

And before someone asks, no, mime-type validation doesn't fix this; it's very easy to create a file that validates as image/jpeg, has a .jpg extension but contains php code.

On a similar vein, do not use file_get_contents, fopen etc. with user input. Someone could very easily abuse this with a few ../s to open your database connection script and find your database password. On the subject of databases, I've covered in my last post the stupidity of using .inc files so I shalnt repeat it.

Mail exploits

Exploting the mail() function is very easy depending on what your using it for. If you're sending a plain text email to one hardcoded address with a hardcoded subject then there's nothing that can be exploited. If you use user input in the From or Subject parameters then someone can inject extra email headers to do things like bcc the mail to them.

The biggie though comes when you use user input in the additional headers parameter. If you're sending an email with attachments or a html email then you will be using this instead of the body parameter to send your message. Someone can inject extra attachments and sections to your email.

Non-plain text emails work by encoding everything into base64 then seperating them with boundaries. They look something like this:

--Boundary1
Content-Type: text/plain; charset=us-ascii

(content)

--Boundary1
Content-Type: text/html; charaset=utf-8

(html content)

--Boundary1--

See, if you know what that Boundary1 is, you can create an extra boundary and add stuff to it. To get around this, just generate a random boundary string every email. That will pretty much screw any attempt to inject stuff.

Notes

Type Casting: PHP doesn't really enforce variables being a particular type and will quite happily convert a variable to another type as and when is necessary. You can force a variable to be a particular type using typecasting. So if you wanted a $_GET variable to always be an integer you'd go:


$_GET['id'] = (int) $_GET['id'];

The (int) bit is the important bit. See the second section of http://uk3.php.net/language.types.type -juggling for more details.

Well that's me spent for now. Comments would be appreciated.

Updated: 07/10/08 10:43 AM 3 comments | Log in to comment! | Share this!
BoneIdol

.inc file extensions...

Posted by BoneIdol Apr. 16, 2008 @ 10:10 AM EDT

I've always been aware that a few people are silly enough to use these, but everyone needs to stop it now as it's embarassing.

I discovered that you can search urls in google in the advanced search today and, for a quick giggle, I decided to search for urls that contained "db.inc". Boy do I wish I hadn't now.

By using .inc extensions you are essentially allowing the whole world to see what's contained in that file. This is pointed out in every single half-decent web programming book and stressed over and over again in papers on website security.

Yet there are still so many people who feel it's a good idea to store their database passwords in a file that any user can see.

I'm frankly astonished that the internet actually works sometimes.

4 comments | Log in to comment! | Share this!