Website Hacking, Part V

Introduction

In this part of the Website Hacking series we are going to take a look at how to minimize damages from XSS attacks considering our web application can at some point become vulnerable to this type of attacks (HttpOnly cookies are going to be discussed). We are going to look at a mechanism for bypassing the security that HttpOnly cookies provide known as Cross Site Tracing attack. We are also going to give a brief example of packet sniffing, followed by a more lengthy discussion on session hijacking and session fixation.

 

 

Minimizing damages from XSS (HTTPOnly Cookies)

If your site at some point becomes vulnerable to XSS, you probably know that your cookies would be exposed. The attacker can simply save document.cookie’s value somewhere and use it for malicious purposes – for example – he can try to steal your session by looking for a session identifier in your cookies. As you know, the main difference between cookies and sessions is that cookies are stored locally on the user’s machine, and sessions are stored in the server (usually the tmp folder, which has to be ensured is out of the public folder and out of reach for regular users). However, the way sessions work is that they save an ID which links the user with the particular session stored on the server, and this session ID (known as magic cookies or session keys) can be stored and passed to the server in a cookie.

Here is one way to make sure that if somebody could get the document.cookie from your users, he does not get all the cookies you have stored for that user.

Read the following setcookie description below (as taken from php.net)

1
bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )

A way to ensure that if an unauthorized person can execute JavaScript on clients he would not get the cookie is to set the last parameter ($httponly) to true. This will disallow JavaScript from reading the cookie that you are setting.

A sample cookie would look something like that:

$username = "Cartman";
$cookie /* true/false */ = setcookie("user2", $username, 
	0 /*expire at the end of the session */,  
	"/"  /* available within the whole domain */,
	 "", false, true /*HTTPonly; no JS access */);

Then you can easily access it with PHP with something like that:

if (isset($_COOKIE[‘user2′])) {
echo “OKAY”;
}

Even better, there is a way to make sure all your cookies are HTTPOnly:

You can set in your php.ini file session.cookie_httponly to 1 or force HTTPOnly cookies only for particular pages/scripts on your website by adding:

// Make session cookies httponly
ini_set('session.cookie_httponly',1); 

in the page’s header.

Another way to add an HTTPOnly cookie (a more low-level one) is to set the header yourself:

// A more low-level way of setting a httponly cookie
header("Set-Cookie: user2=Kenny; httpOnly");

HTTPOnly Cookies circumvention and prevention (XST)

We have seen that HTTPOnly cookies disallow access to the cookies from JavaScript. However, a technique was crafted to circumvent this protection. It is known as XST or Cross Site Tracing Attack.

The attacker could target old browsers and type something similar to:

<script>

//Setting an AJAX TRACE request method to get cookies that cannot be seen with JavaScript
try {
var xhr = new XMLHttpRequest();

}

catch (e) {
var xhr = new XDomainRequest();
}

xhr.open(‘trace’, “http://localhost:8079/WebsiteHacking5/code.php”, false);
xhr.send();
var traceData = xhr.responseText;
</script>

After which the variable traceData would contain the cookies he desires as well as other data about the request. However, in contemporary browsers the TRACE request method is disabled for AJAX requests by default (throws a security error).

Similar AJAX XST attempts should not work on clients having Firefox 19.0.2+ or Chrome 25.0.1364.172+

Thus, it may still be useful to block Trace requests.

To disable it natively, Apache’s httpd.conf must be opened and the following line added to it:

TraceEnable off

This would work on versions of Apache > 1.3.34 or 2.0.55+ for apache2.

Another way to disable TRACE methods without changing the htpd.conf file is changing the .htaccess file and adding:

RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* – [F]

Note: mod_rewrite has to be enabled for the snippet to work.

Packet sniffing

There are many sites out there whose client credentials are vulnerable when it comes to man-in-the-middle attacks. When connected to a public Wi-Fi, there could easily be someone at that restaurant sniffing the incoming traffic. Here is a sample using Wireshark:

Firstly, we have applied an HTTP filter to only see HTTP requests.

Then we use Ctrl+F, search for a String and enter “POST” for value to only see the POST requests (in most cases, those contain user-entered data and frequently interesting ones).

Here we see a sample of unencrypted client/server communication after a registration form has been submitted.

A fix for that is using HTTPS (TLS/SSL).

Session hijacking/Session fixation

Assuming another vulnerability is present (such as XSS), an attacker can see users’ cookies, then he can see which one is the session ID (by default in Apache servers the session id is carried in a cookie called PHPSESSID and hijack an active session. Below we have provided an explanation of how that could happen. Assume we have this login script and login form on the same file (in a real application the accounts would be populated from a database).

We can see that in the upper left corner there is a paragraph with the text “User appears NOT logged in” before the user has been logged in.

Note: we have enhanced the form visually a bit with Bootstrap 3.

The code is as follows:

session_start();
if (isset($_SESSION['account'])) {
	echo "<p>User appears logged in.</p>";
}
else {
	echo "<p>User appears NOT logged in.</p>";
}



?>

<!DOCTYPE html>



<html lang="en">
<head>
	<link rel="stylesheet" href="css/bootstrap.min.css">
	<meta charset="UTF-8">
	<title>Login</title>
</head>
<body>
	<div class="container">
		<div class="row">
			<div class="col-lg-6 col-lg-offset-3">	
				<form class="form form-vertical" action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method="POST">
				<div class="input-group">
					<span class="input-group-addon glyphicon glyphicon-user"></span>
					<label for="username">Username:</label>
					<input name="username" id="username" class="form-control input-lg" type="text">
				</div>
				<div class="input-group">
				<span class="input-group-addon glyphicon glyphicon-lock"></span>
				<label for="password"> Password </label>
				<input type="password" class="form-control input-lg" name="password" id="password">			
				</div>
				<div class="submit-container text-center">	
						<input type="submit" style="margin-top: 15px;" class="btn btn-lg btn-success" value="Log in">
				</div>
				
				</form>

			</div>
		</div>

<?php


$accounts = array("Cartman" => "overweight", 
	"Kenny" => "diesoften",
	 "Kyle" => "jew", 
	 "Chef" => "ladiesguy");


if ($_SERVER["REQUEST_METHOD"] === "POST") {
	$username = $_POST['username'];
	$pass = $_POST['password'];
	foreach ($accounts as $account => $accountPass) {
		if ($account == $username) {
			if ($accountPass == $pass) {
				echo "<h1 class='text-center'>Logged in as $account!. Redirecting... </h1>";
				$_SESSION["account"] = $account;
				sleep(1);
				header("Location: memberzone.php");
			}
				
			}

		}

		 if (!isset($_SESSION["account"])) {
		 	echo "<h1 class='text-center'>Wrong credentials.</h1>";
				
	}
		 }
?>


		</div>
	</body>
</html>

We do not implement an actual attack but check our session ID cookie:

Then we open the page in a browser that does not have any active sessions, start Tamper Data and submit the form with wrong credentials, but use the session ID of the actual logged in user.

The result is: we have hijacked the session of a legitimate user and appear logged in. (We do not get redirected, but as you can see we are logged in and there’s no wrong credentials statement appearing).

Actually, as you can see we are not redirected to the memberzone, so the code inside the if ($accountPass
==
$pass) conditional does not execute, but if we have code executed in another block, which depends on the user being logged in or similar, then the attacker would have been able to run the code inside it and do whatever he wants with the user’s account.

The first and more ineffective way of protecting against such attacks is to set session.referer_check = yourDomainHere in php.ini or use ini_set to set it for a particular script. In that way, the session would be set only if the set domain/path is the referrer (equivalent to $_SERVER[‘HTTP_REFERER’] regular expression search in a sense and not a very effective method). Plus, it does not accept multiple parameters. Multiple parameters are supported in some frameworks such as CakePHP.

What you should do is protect yourself against XSS attacks, as these can leak the session ID and lead to session hijacking. You should also use httpOnly cookies to revoke JavaScript access to your cookies, possibly minimizing damages from XSS and not leaking users’ cookies.

Another thing you can do is to check if the IP address of the person who first logged in and the IP address of the person using the session are the same (they are expected to be the same).

Firstly, we make the following changes (check if there was a session hijacking attempt, say to the user that he has been reported and send an email):

if (!isset($_SESSION[“account”])) {
echo “<h1 class=’text-center’>Wrong credentials.</h1>”;}
else {if ($_SESSION[‘IP’] !== $_SERVER[“REMOTE_ADDR”]) {
echo “<h1> Security breach attempt. Contacting the administrator”;
mail(“test@mail.bg”, “Security Breach”, $_SERVER[“REMOTE_ADDR”] . ” attempted to hijack the session of a user!\r\n”);

}

}

In our loop that checks whether some of the credentials match, we have to also log the IP address of the person at the moment of logging him in.

$username = $_POST['username'];
	$pass = $_POST['password'];
	foreach ($accounts as $account => $accountPass) {
		if ($account == $username) {
			if ($accountPass == $pass) {
				echo "<h1 class='text-center'>Logged in as $account!</h1>";
				$_SESSION["account"] = $account;
				$_SESSION['IP'] = $_SERVER["REMOTE_ADDR"];
	
			    }
				
			}

		}

And we modify the section when we show content only for logged in users to something like:

session_start();
if (isset($_SESSION['account']) && $_SESSION['IP'] === $_SERVER["REMOTE_ADDR"]) {
	
	// Code to be executed for logged in users can be placed here
	echo "<p>User appears logged in.</p>";
}
else {
	echo "<p>User appears NOT logged in.</p>";
}

We have logged in with Chrome as Kenny.

We use a different IP address and try to steal the session with the session ID cookie credentials using Tamper Data.

We get the following page:

User appears NOT logged in (we have not fooled the script) and a notification that the breach was found is displayed on the screen. And if we look at our email client, we will get a message resembling this but with the full IP address of the attacker written in the email’s contents:

Session fixation is different from session hijacking because in session hijacking the attacker could intercept or hijack the session’s cookie, but in session fixation the attacker supplies the user with a session ID that is already set, and when that user logs in the server would assign his data into the session ID that is set by the attacker, which would grant him access to the user’s session.

To prevent session fixation, one way is to check the referrer (we have mentioned that before). Another way is to reduce the duration for which sessions are active from another php.ini setting (session.cache_expire).

However, what we recommend is that you regenerate session IDs frequently.

Assigning a new session ID will ensure that the ID that a possible attacker has obtained would not be useful, because a new session ID would have been assigned to the user.

We can achieve that with the following snippet:

session_start();
if (!isset($_SESSION['check'])) {
 	session_destroy();
 	session_regenerate_id();
	$_SESSION['check'] = 1;
} 

Conclusion

In this article, we have examined some common exploits out there, and we hope that you will start implementing some of these security mechanisms in your future projects, if you are not implementing them already.

Sources:

  1. MDLog:/sysadmin, “Apache Tips: Disable the HTTP TRACE Method”, Accessed 3/12/2014, Available at:http://www.ducea.com/2007/10/22/apache-tips-disable-the-http-trace-method/
  2. OWASP, “Cross Site Tracing”, Accessed 4/12/2014, Available at:https://www.owasp.org/index.php/Cross_Site_Tracing
  3. Stack Overflow, ‘What is PHP’s session.referer_check protecting me from?’, Accessed 5/12/2014, Available at:http://stackoverflow.com/questions/4863946/what-is-phps-session-referer-check-protecting-me-from/

This article was originally published on InfoSec Institute

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *


five + 8 =