Showing posts with label PHP. Show all posts
Showing posts with label PHP. Show all posts

Tuesday, February 20, 2007

Gotcha!: A PHP Oddity (with contrived security implications)

A while ago, I was look over some code a friend of mine (who doesn't write much PHP) had written, which 'worked', but really shouldn't have.

It looked something like this:

if (strpos ($string, "needle") == "needle") {
    print "Is Valid";
}


And if you know PHP, you'll know that strpos always returns an integer or Boolean false (i.e. it should never return a string), so how the hell could this work?

Well, skipping my usual anecdote; it turns out that PHP casts string to integers when doing comparisons between strings and integers (not the other way around as I would have expected), and so "needle" got cast to an integer, and it was equal to 0. (And since strpos was returning 0 the above code worked).

<Edit> (21/02/07): I realise that there should never be a double dollar vulnerability anywhere in your code, but mistakes are made, this is just what I thought was a curiosity which would interest people; clearly I was wrong. Also, while this uses a double dollar vuln, it is the only way I could come up with to get a string to be compared to an integer (rather than a decimal string) you control.
</Edit>

Now, for the very contrived security issue:

<?php

$authenticated = 0;
session_start();

if (isset($_SESSION['password'])) {
    if ($_SESSION['password'] == "password removed from backup") {
        $authenticated = 1;
    }
} elseif (isset ($_GET['password'])) {
    if ($$_GET['password'] == "password removed from backup") {
        $authenticated = 1;
    }
}

if ($authenticated == 1) {
    print "You Win!";
} else {
    print "You Fail!";
}

?>


You'll see the code above has a double dollar vulnerability; which would be unexploitable in this scenario because the password string is not stored in a variable; but rather is hard coded. But since; at this point, the variable $authenticated is equal to zero, we can have $_GET['password'] equal to "authenticated", and $$_GET['password'] is equal to zero, and the comparison works.

Note: The double dollar vuln is needed rather than just passing 0 to a normal comparison because all variables sent through http are strings.

Note2: It doesn't matter in what order the arguments are typed in the comparison, i.e. the following would also be vulnerable:
if ("password removed from backup" == $$_GET['password']) {

Thursday, November 23, 2006

Not all redirection scripts are created equal.

Now, I'm sure we're all used to redirection scripts by now, and most of us should be aware of the danger of HTTP Response Splitting when allowing user content in the headers we send, and that there is also a patch in PHP since 4.4.2 and 5.1.2 which prevents this by dissalowing multiple lines in a single header call. This patch is not completely bullet proof as articles such as HTTP Response Smuggling illustrate, but sometimes there's an easier way to achieve what you want.

First of all, what is it that HTTP Response splitting gets most used for anyway? XSS. So any injection that will give us an XSS vector is just as good as any other.

But like the title says, not all redirection scripts are created equal. The most common approach used to redirect people is somewhat similar to the following:

<?php
header ("Location: ".$_GET['url']);
?>


Which, when the PHP patch is applied, should be perfectly safe when ti comes to preventing XSS.

But is that the only approach? As some recent auditing I've done has shown me, its not.

I found something like the following not too long ago:

<?php
header ('refresh: 0; URL="'.$_GET['url'].'"');
?>


And while it may serve the same needs as the above it, its surprisingly different. But before I explain the exact problem, does the header look familiar to you? It should, its the header that is used in meta tags to redirect users, and if you've done much reading on XSS you should know that meta redirects are another way to execute javascript.

And unsurprisingly enough, the above header follows the same rules. The refresh header, unlike the location header allows you to redirect users to javascript: or other URLS, depending on what your browsers support, so it should be no trouble at all to simply pass a javascript url to the script and have the user execute the javascript.

Just goes to show how there is generally more than one way to do something, but going down an untested road will often lead to unforseen problems that have already been solved for other approaches.

Monday, April 24, 2006

Validation in Context

Its been a while since I posted anything here, but considering I'm fairly sure no-one actually reads this and I had nothing new to write that hadn't been written elsewhere (or at least I haven't seen written elsewhere), I haven't really bothered writing much more, but after finding an XSS hole relating to this topic today i thought I'd write about it.

======================
Validation in Context
======================

==============
Contents
==============

  - 1.0 Introduction
  - 1.1 What is context?
  - 1.2 Why is context important?

  - 2.0 The contexts of SQL Injection

  - 3.0 The contexts of XSS

  - 4.0 Conclusion


================
1.0 Introduction
================
In this article I would like to explain the need for the need of validation (and encoding/escaping) in context. The need for validation is one that is often known only by a few implications, and usually performed either prematurely or inadequately. One of the great sources of inadequacy is encoding and validation out of context.

==============
1.1 What is context?
==============
Context is, in the most simple of terms, is the circumstances in which something occurs. For the sake of this article we will refer to context as the circumstances in which operations are performed on user input. An understanding of context is essential to good input validation, encoding/escaping etc (simply referred to as validation from here-on-in).

==============
1.2 Why is context important?
==============
Context is important because we are trying to somehow pass user input to extremely complex systems, be they relational database management systems (RDBMS), browsers or operating systems, without the complex system performing any unexpected actions.

In the simplest of terms, context is important because its (generally) pointless to use a method used in one situation on another situation, e.g. escaping quotes is pointless in stopping XSS, just as escaping < and > symbols is useless in stopping SQL Injection.

==============
1.3 Understanding and finding contexts
==============
For us to be able to understand and find contexts we must be intimately familiar with the system into which we are feeding user input. And furthermore we must be able to know how much capacity for error the system will allow. The easiest example of contexts which we can find would be in SQL injection, which I will cover in the next section.

================
2.0 The contexts of SQL Injection
================
To find contexts in SQL Injection we must first understand our queries. For example, if we are placing user input inside single quotes, it is pointless for us to only escape double quotes and vice versa. And so we have our first 2 states:

  - An expression inside single quotes
  - An expression inside double quotes

But an expression does not have to be inside quotes at all, we could have an integer which is outside quotation marks because it then does not force the RDBMS to evaluate the string and convert it to a number, and in this case our normal validation of escaping quotes (either one or both) would be of extremely limited use in stopping an attacker because the attacker does not need to use quotes to break out of the expression, and does not need quotes for almost all other actions they would like to perform. And so now we have our third context:

  - An un-enclosed expression

But since we know that the value we are expecting is an integer; validating it is not all that difficult, but cases like these prevent solutions like magic_quotes_* (as in PHP's case) from working completely, and begin to illustrate why validation needs to be done in context rather than in a way which stops a few vectors.

In SQL the contexts are few, but do help to illustrate how context needs to be taken into account.

================
3.0 The contexts of XSS
================
In XSS attacks you are passing user input to a much more complex (in terms or input parsing) system than an RDBMS, you are passing user input to a browser. The most basic context people protect against is when user input is placed on the page inside a container (be it a tag or the page itself acting as a container), so we have our first context:

  - User input inside a container (either a tag, or the page itself)

and the most common method (in PHP, at least) is to simply run everything through either strip_tags() (which as the name implies strips - without any additional parameters - all tags from user input), or the slightly more useful htmlentities() (which encodes all characters which have HTML character entity equivalents are translated into these entities, e.g. < is converted to < and " is converted to ", etc - encoding of quotes though,is optional -, htmlspecialchars() does much the same thing for our purposes, but it only does a few characters, those characters being <>&'" ).

Again, there are more contexts, but the same functions are often relied upon to neutralize threats in several circumstances. The next circumstance would be when user input is placed inside an attribute for a tag, e.g. :

print "<body bgcolor=\"".our_encoding_function($_GET['bg'])."\">";

In this case, using htmlentities() (or htmlspecialchars() ) as our_encoding_function() will still work, but strip_tags() will not, because what we need to filter out or encode here are double quotes, because it would be trivial for an attacker to pass this:

" onLoad="insert malicious javascript here

and be able to execute code without having any tags stripped because the attacker had no need to use tags. But not all attributes are enclosed in double quotes, lets say the developer favoured single quotes and did this:

print "<body bgcolor='".our_encoding_function($_GET['bg'])."'>";

then all our encoding (when supplied with no other parameter then the user input) becomes useless because by default htmlentities() and htmlspecialchars() do not encode single quotes. But even if it was set to encode single quotes it would not help us in the smallest if the developer had not encapsulated user input at all, like so:

print "<body bgcolor=".our_encoding_function($_GET['bg']).">";

, because then we would not need to use quotes ourselves. Admittedly it is much harder to create a javascript payload without using quotes or whitespace, but it is still doable. From here we have 3 new  contexts:

  - User input inside an attribute to a tag, enclosed with double quotes
  - User input inside an attribute to a tag, enclosed with single quotes
  - User input inside an attribute to a tag, un-enclosed (could also be described as being enclosed with whitespace)

It also matters inside what *kind* of attribute the user input exists. For example if the attribute is merely the background color of the page, then nothing harmful can come about (without of course a bug in the browser) without the user breaking out of the attribute. But what if the tag was a javascript tag, and the user input was enclosed within single quotes inside the javascript, and the attribute was enclosed in double quotes like so (while its not the most likely real world example, it does illustrate a point without having to discuss browser differences):

print "<body onLoad="alert('.our_encoding_function($_GET['bg'])."');\">";

then nothing short of encoding, single and double quotes would suffice (we don't need to worry about > symbols because they can exist inside attributes), because with anything less the user would be able to  break out of either the single quotes encapsulating the string inside the alert statement, and therefore allowing the user to execute arbitrary (well almost) javascript commands, or (because of a browser's permissive nature) break out of the attribute altogether then creating a new attribute with other javascript, and we have XSS all over again. And the same situation could be reversed in a few ways and we get the following contexts:

  - User input inside some javascript, encapsulated by single quotes, inside an attribute to a tag, enclosed with double quotes
  - User input inside some javascript, encapsulated by single quotes, inside  an attribute to a tag, enclosed with single quotes
  - User input inside some javascript, encapsulated by single quotes, inside  an attribute to a tag, un-enclosed (could also be described as being enclosed with whitespace)

  - User input inside some javascript, encapsulated by double quotes, inside an attribute to a tag, enclosed with double quotes
  - User input inside some javascript, encapsulated by double quotes, inside an attribute to a tag, enclosed with single quotes
  - User input inside some javascript, encapsulated by double quotes, inside an attribute to a tag, un-enclosed (could also be described as being enclosed with whitespace)

And so we have so many more possible contexts to keep in mind, luckily though it can be simplified down to the following rule:

    - Encode single and/or double quotes depending on whether or not they are used as encapsulation.

We could of course include contexts like being inside CSS, etc, etc, but these are the main ones, and this article is designed to raise awareness of contexts rather than to be a definitive list of them.

The contexts so far presented though have been quite similar, lets have a look at something a bit more esoteric. lets say we have a piece of javascript which is setting a cookie, the input doesn't need to come from a server-side script, it can be done by javascript, and it does not matter because we are not trying to break out of what the javascript is trying to do, we just want to do a bit more than what its intended to do. but for the sake of simplicity lets say we're using the following server-side script:

print "<script>document.cookie = \"name=".htmlentities($_GET['name'], ENT_NOQUOTES)."\";</script>";

now, beside the fact that there is no really sane reason (that I can think of) you would be setting server-side cookies using this method, this is only an esoteric example to show you that you should not confine yourself to the common. The reason is that while we cannot escape any of the boundaries (either the string encapsulation or script tag) we can still cause unexpected behavior by setting other cookie. Lets say we set the name variable to:

Alex;\nid=0;

, then the resulting javascript will become:

<script>document.cookie = "name=Alex;\nid=0;"; </script>

which causes us to set a new cookie (we could overwrite an existing cookie as well) besides the name cookie, and we could use such injection to perform things like $_REQUEST Variable Fixation (http://kuza55.blogspot.com/2006/03/request-variable-fixation.html), or other attacks. But more than to show an actual attack, it is an example of how the context (in this case; setting a cookie) will allow you to perform attacks, where the validation needed to stop those attacks would be useless and restrictive in other cases. And it satisfies my need for an obscure attack vector, :p.

================
4.0 Conclusion
================
What I've outlined here is not really new information, it is merely something which is greatly neglected by many people writing web apps. If there was anything I would like you to take away from this article above all else, it would have to be; know your encapsulation and how it can be broken out of, and how unexpected behaviours can be created. There's really nothing more to it other than remembering that, and not forgetting to apply that knowledge, and not just hope that nobody will notice.

- kuza55

Monday, March 13, 2006

Format Strings in PHP

I realise that no-one actually reads what I post here, but i've got an urge to post the crap that i've written up and posted almost nowhere (and in this case nowhere at all) for some reason, even thugh its crap, and useless....

================
Format Strings
================

This small article pertains to Format String vulnerabilities in PHP applications rather than PHP itself.

Format String Vulnerabilities exist where the user is allowed to place data into a format string of a function which accepts a format string, e.g. sprintf(). These vulnerabilities are uncommon, even rare, in PHP because these functions are generally not used in PHP scripts.

In most other languages format string vulnerabilities allow:
- Viewing the Stack
- DoS attacks
- Improper validation checks

In PHP the first 2 should not be an issue since PHP makes sure the amount of unique (in the sense that they are unique if they don't reference to the same variable passed to the format function) place holders and variables it is passed, or provides an error message otherwise, and since the DoS vulnerabilities in format strings are resource exhaustion/corruption and PHP has a setting for the maximum amount of RAM it can consume (after it surpases the limit it simply kills the script and provides an error, instead of crashing) and the corruption can occur when the user is able to force the program to write to the stack, but since you cannot write to the stack using PHP's format functions those vulnerabilities do not exist.

Improper validation checks are probably the only things you need to worry about in PHP, and then only if you were fairly sloppy in your work. An example of a vulnerable script would be a script which INSERTs data into a database, which validates different fields in different ways, and therefore trusts them differently when uing data retrieved from those fields:

<?php

// Our RegExps to validate our data, the only thing that is required here is that one allows
// single quotes (or whatever is used to encase strings) and of course letters so that stored
// SQL Injection can be performedand and another be allowed percent and dollar signs and of
// course lowercase letters.
$regexps['field1'] = '/^A-Za-z0-9\'+-=!@#$%^&*()$/';
$regexps['field2'] = '/^A-Za-z0-9+-=!@#$%^&*()$/';

$errors['field1'] = "The value you supplied for field1 did not match the following RegExp: /^A-Za-z0-9'+-=!@#$%^&*()$/\n";
$errors['field2'] = "The value you supplied for field2 did not match the following RegExp: /^A-Za-z0-9+-=!@#$%^&*()$/";

$errormsg;
$err = 0;

foreach ($regexps As $key=>$value) {
if (!ereg ($regexps[$key], $_POST[$value])) {
$err = 1;
$errormsg .= $errors[$key];
}
}

if (empty($err)) {
$dbh = mysql_connect();
$sql = sprintf ("INSERT INTO table (field1, field2) VALUES ('%s', '".mysql_real_escape_string ($_POST['field2']."')",
mysql_real_escape_string($_POST['field1']));
mysql_query


}

?>


In this (highly simplified example) we want to allow users to enter single quotes into field1 for some reason, e.g. its a surname field and we want to allow names like O'Rielly, and for some reason we want to allow percent signs in field2, and we were sloppy and inserted the (already validated) value of field2 directly into the format string.

Now, how could this be bad, well it all comes back to the issue of trust, you trust that because you validated field2 to make sure there were no quotes and you do that validation every ime the user wants to update that field so you decided you weren't going to escape it to increase performance, now lets see what happens when we try and exploit the format string bug.

Lets say we provided some stored SQL Injection (this could also be done with stored XSS) code for field1, it wouldn't work since we know that field 1 could contain malicious data so we always escape it, to field2 though we provide the following value:%1\$sWhich is the format for inserting the first variable passed to the function after the formt string, and all of a sudden our format string looks like this:INSERT INTO table (field1, field2) VALUES ('%s', '%1\$s')And all of a sudden we have some malicious SQL stored in a field which you trust implicitly and don't escape.

Thursday, March 09, 2006

$_REQUEST Variable Fixation

===================================
$_REQUEST Variable Fixation
===================================

The issue described here is one that also affects register_globals, but unlike register_globals there are no plans to remove $_REQUEST. This issue is not an exploit in itself, but is one that developers should be aware of nonetheless.

=============
Background
=============

The issue with $_REQUEST is not some fault, but it is an issue it has by virtue of what it attempts to do. $_REQUEST is populated from the same sources as the superglobal arrays defined in the php.ini setting variables_order, here is php.net's explanation of what it affects:

    Set the order of the EGPCS (Environment, GET, POST, Cookie, Server) variable parsing. The default setting of this directive is "EGPCS". Setting this to "GP", for example, will cause PHP to completely ignore environment variables, cookies and server variables, and to overwrite any GET method variables with POST-method variables of the same name.

In the default setting (EGPCS) $_REQUEST is first populated with the data of $_ENV, and then with $_GET, and if there are any keys which are the same the keys already in $_REQUEST are overriden with those in $_GET, and then the process is repeated for $_POST, $_COOKIE and $_SERVER.

=============
The Issue
=============

The issue is that if an attacker is able to set a cookie with the same key as say a form element or GET variable, then the script will act on the value in the cookie untill it is removed.

Now, what actual applications can this have you ask? Well, imagine someone finds an XSS flaw in an otherwise completely secure application, where even if you obtain the session ID via this XSS hole you won't be able to do much because only one session can be active at a time, and the session id gets regenerated on every page, and the session is also bound to the user's IP, so even if you have the cookie data there isn't much you can do, and the users of the target application are generally careful enough to only enter their password in the proper place (so using the XSS hole to create a username/password form would be out of the question).
Now if this application were lets say, a bank's site, and they used $_REQUEST for getting data from the user, and the attacker then set a cookie with the key the same as the key of the field where the user enters the bank account ID where to transfer money, and sets the cookie's value to an account number which corresponds to an account under the attacker's control.

Another situation where this could be employed would be when a site uses $_REQUEST to get a new password from a user when allowing users to change their password, and you can set a cookie to make the application set a password of your liking.

=============
Conclusion
=============
While this is an interesting idea in my view, it is probably one of low severity, and its actual applications would be limited, so while its nothing to start auditing your code over it would be wise to either edit the variables_order directive to remove cookies and server variables, and maybe even environment variables, because there is only a very limited need for applications which can support input through many different methods (i.e. $_POST, $_GET, $_COOKIE, $_ENV, $SERVER), and it would be wise to reconsider your need for it, but if you decide you do need it then there is no real reason not to, though it is an issue you should be aware of.


   - kuza55