Session Hijacking Basic

EDB-ID:

15990

CVE:

N/A




Platform:

PHP

Date:

2011-01-14


 __               _                      _  _            _    _             
/ _\ ___  ___ ___(_) ___  _ __     /\  /(_)(_) __ _  ___| | _(_)_ __   __ _ 
\ \ / _ \/ __/ __| |/ _ \| '_ \   / /_/ / || |/ _` |/ __| |/ / | '_ \ / _` |
_\ \  __/\__ \__ \ | (_) | | | | / __  /| || | (_| | (__|   <| | | | | (_| |
\__/\___||___/___/_|\___/|_| |_| \/ /_/ |_|/ |\__,_|\___|_|\_\_|_| |_|\__, |
                                         |__/                         |___/ 
							      Basic

# language: English
# Title: Session Hijacking Basic
# Date: 2011-01-13
# Author: Filipe Barros/@barros_filipe	

| +01 - Session Fixation
| +02 - Session Hijacking
| +03 - Firesheep

Have fun :)

====== +01 - Session Fixation ======

The attacker attempts to gain access to another user's session by posing as that user.

The information for an attacker is the session identifier, because this is required for any impersonation attack. There are three common methods used to obtain a valid session identifier:

* Fixation

* Capture

* Prediction

Prediction refers to guessing a valid session identifier. With PHP's native session mechanism, the session identifier is extremely random, and this is unlikely to be the weakest point in your implementation.

Because session identifiers are typically propagated in cookies or as GET variables, the different approaches focus on attacking these methods of transfer. While there have been a few browser vulnerabilities regarding cookies, these have mostly been Internet Explorer, and cookies are slightly less exposed than GET variables. for those users who enable cookies, you can provide them with a more secure mechanism by using a cookie to propagate the session.

Fixation is the simplest method of obtaining a valid session identifier. While it's not very difficult to defend against, if your session mechanism consists of nothing more than session_start(), you are vulnerable.

To demonstrate session fixation, I'll use the following script, session-hijacking.php:

[ Begin PHP CODE ]

<?php

session_start();

if (!isset($_SESSION['visits']))
{
    $_SESSION['visits'] = 1;
}
else
{
    $_SESSION['visits']++;
}

echo $_SESSION['visits'];

?>

[ End PHP CODE ]

First make sure that you do not have an existing session identifier (perhaps delete your cookies), then visit this page with ?PHPSESSID=123456789 appended to the URL. Next, with a completely different browser (or even a completely different computer), visit the same URL again with ?PHPSESSID=123456789 appended. You will notice that you do not see 1 output on your first visit, but rather it continues the session you previously initiated.

If there isn't an active session associated with a session identifier that the user is presenting, then regenerate it just to be sure:

[ Begin PHP CODE ]

<?php

session_start();

if (!isset($_SESSION['initiated']))
{
    session_regenerate_id();
    $_SESSION['initiated'] = true;
}

?>

[ End PHP CODE ]

The problem with such a simplistic defense is that an attacker can simply initialize a session for a particular session identifier and then use that identifier to launch the attack.

====== +02 - Session Hijacking ======

If your session mechanism have only session_start(), you are vulnerable.

With the most simplistic session mechanism, a valid session identifier is all that is needed to successfully hijack a session. In order to improve this, we need to see if there is anything extra in an HTTP request that we can use for extra identification.

Recall a typical HTTP request:

GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 Gecko
Accept: text/xml, image/png, image/jpeg, image/gif, */*
Cookie: PHPSESSID=123456789
Only the Host header is required by HTTP/1.1, so it seems unwise to rely on anything else. However, consistency is really all we need, because we're only interested in complicating impersonation without adversely affecting legitimate users.

Imagine that the previous request is followed by a request with a different User-Agent:

GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla Compatible (MSIE)
Accept: text/xml, image/png, image/jpeg, image/gif, */*
Cookie: PHPSESSID=123456789

Although the same cookie is presented, should it be assumed that this is the same user? It seems highly unlikely that a browser would change the User-Agent header between requests, right? Let's modify the session mechanism to perform an extra check:

[ Begin PHP CODE ]

<?php

session_start();

if (isset($_SESSION['HTTP_USER_AGENT']))
{
    if ($_SESSION['HTTP_USER_AGENT'] != md5($_SERVER['HTTP_USER_AGENT']))
    {
        /* Prompt for password */
        exit;
    }
}
else
{
    $_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
}

?>

[ End PHP CODE ]

Now an attacker must not only present a valid session identifier, but also the correct User-Agent header that is associated with the session. This complicates things slightly, and it is therefore a bit more secure.

Imagine if we required the user to pass the MD5 of the User-Agent in each request. An attacker could no longer just recreate the headers that the victim's requests contain, but it would also be necessary to pass this extra bit of information. While guessing the construction of this particular token isn't too difficult, we can complicate such guesswork by simply adding an extra bit of randomness to the way we construct the token:

<?php

$string = $_SERVER['HTTP_USER_AGENT'];
$string .= 'SHIFLETT';

/* Add any other data that is consistent */

$fingerprint = md5($string);

?>

Keeping in mind that we're passing the session identifier in a cookie, and this already requires that an attack be used to compromise this cookie (and likely all HTTP headers as well), we should pass this fingerprint as a URL variable. This must be in all URLs as if it were the session identifier, because both should be required in order for a session to be automatically continued (in addition to all checks passing).

In order to make sure that legitimate users aren't treated like criminals, simply prompt for a password if a check fails. If there is an error in your mechanism that incorrectly suspects a user of an impersonation attack, prompting for a password before continuing is the least offensive way to handle the situation. In fact, your users may appreciate the extra bit of protection perceived from such a query.

There are many different methods you can use to complicate impersonation and protect your applications from session hijacking. Hopefully you will at least do something in addition to session_start() as well as be able to come up with a few ideas of your own.

====== +03 - Firesheep ======

Recently a firefox extension called Firesheep has exploited and made it easy for public wifi users to be attacked by session hijackers. Websites like Facebook, Twitter, and any that the user adds to their preferences allow the firesheep user to easily access private information from cookies and threaten the public wifi users personal property.

Firesheep is free, open source, and is available now for Mac OS X and Windows. Linux support is on the way.

Websites have a responsibility to protect the people who depend on their services. They've been ignoring this responsibility for too long, and it's time for everyone to demand a more secure web. My hope is that Firesheep will help the users win.

Thanks!

:)