Introduction

To view Part I of this article, please visit http://resources.infosecinstitute.com/website-hacking-101/.

In this Part, we are going to briefly introduce Path Traversal, usage of Delimiters, and Information Disclosure attack.

We are going to present simple solutions to simplified problems involving the attacks.

Content

Exercise 8: Path Traversal

Figure : A simple webpage in which you choose an article and view it

The website (index.php) in the PathTraversal folder contains a simple form which submits to the same page through the GET request method. Once a choice of article has been made and “View article” has been clicked, the following PHP code executes:

<?php
//If the article GET parameter is set
if (isset($_GET["article"])) {
// Create a div block and fill it with the contents from the file in the GET value.
	echo "<div id='article'>" . file_get_contents($_GET["article"]) . "</div>";
}
?>

The result is the following URL: http://localhost/2/PathTraversal/?article=1.htm

It loads the relevant article file placed in the GET method. The parameter article is formed via:

<select name="article" required=""></select>

And the values are also directly given through the HTML code (the value attribute):

Domain Slamming

Now, legitimate users will use the interface provided in the website to browse it, but with the code as it is we can easily open myriad files they do not want you to open by directly tampering with the URL parameters. Many websites have config directories where they store important data – let’s see if you can do it.

Tasks

  1. Go back one directory and open openme.txt by changing the URL parameters.
  2. We assume that we cannot open the folder config from our computer but only from the local server. Assume you do not know what files there are in the directory. First, you should check whether the directory exists.

The directory exists and now we know that there is HTTPAuth in place. Your task is to somehow find out the username and the hashed password for the folder without using any brute-force or dictionary attacks on the username and password.

Spoiler (Task 2)

If we know that there is a HTTPAuth security mechanism in place, then we can automatically deduce there is an .htaccess file. Therefore, we can open the .htaccess file that we would not be able to open normally via the path traversal vulnerability of the article viewer page.

Figure: Viewing the .htaccess file from the article viewer page

We type http://localhost/2/PathTraversal/?article=config/.htaccess and now we know the path and the file in which accounts and passwords are stored as well as the user that is required to view the folder.

We type the path to the userlist.htpasswd file and get all usernames and passwords:

tomburrows:$apr1$ZF.78h2N$zhAaP2AY6VwxuELizJAwg.

Now, the username is known and we have incredibly reduced our cracking time. HTTPAuth is using UNIX’s “CRYPT” function to encrypt the passwords which is a “one way” encryption method.

Using path traversal, we can also go back several directories and browse to the php.ini and other important configuration files as well.

A sample solution to our path traversal vulnerability

<?php
//If the article GET parameter is set

if (isset($_GET["article"])) {
//Remove any “/” and “.” characters from the GET parameter’s value as this can be used for path traversal 
	$article = str_replace(array("/", "."), "", $_GET["article"]);
// If the file does not exist, print a custom error.
	if (!file_exists($article . ".htm")) {
	echo "<h1>The article does not exist!</h1>";
	}
	else {
//If and only if the file exists – echo out its contents

// Create a div block and fill it with the contents from the file in the GET value.
//Add a mandatory file extension of .htm to the file
	echo "<div id='article'>" . file_get_contents($article . ".htm") . "</div>";
	}
}

The change in the HTML code is that we no longer use the full file name value in the options tags, we just use the name of the file (without its extension so only .htm files would be allowed)

 Keyloggers: How They Work and More

Firstly, checking if the file exists and echoing it out only if it exists prevents another attack – that of information disclosure.

There is a PHP warning thrown out if we type a non-existent file deliberately. Of course, another way to resolve such information disclosure issues is by turning off the display_errors In the php.ini file (this is most desirable if the site is live anyway).

With the above mentioned code we get a clean and neat error that the article does not exist, along with prevention of any path traversal attempts.

Want to learn more?? The InfoSec Institute Ethical Hacking course goes in-depth into the techniques used by malicious, black hat hackers with attention getting lectures and hands-on lab exercises. While these hacking skills can be used for malicious purposes, this class teaches you how to use the same hacking techniques to perform a white-hat, ethical hack, on your organization. You leave with the ability to quantitatively assess and measure threats to information assets; and discover where your organization is most vulnerable to black hat hackers. Some features of this course include:

  • Dual Certification - CEH and CPT
  • 5 days of Intensive Hands-On Labs
  • Expert Instruction
  • CTF exercises in the evening
  • Most up-to-date proprietary courseware available

Figure: We now receive an error when we try to go back one directory and open the openme.txt file

Note: in old editions of PHP (older than 5.5.3) you could use the %00 marker to end the string abruptly and pass your own file extension in place of the “.htm” one in our solution code.

if (!file_exists($article . “.htm”)) could be exploited in older versions of PHP by typing:

http://localhost/2/PathTraversal/?article=accounts.txt %00

Which is equivalent to:

“accounts.txt.htm” forcing the server to ignore the .htm part of the string.

Exercise 9: Information disclosure

Figure: Comment page

For this exercise, I have created a working but problematic comments page which looks similar to a chat. You have to write a comment, and then you view all the comments up to now. The comments are stored in a .txt file rather than in a database and there is one PHP file that creates new comments and one that displays them on the screen.

//Index.php server-side code

	<?php
		$path = "comments/";
		?>

		<?php 
			if ($_SERVER["REQUEST_METHOD"] === "POST") {
				include("add_comment.php");			

			}

//Add_comment.php
<?php 
			//Open file and create an array with all comment information as indices
			$comments = file_get_contents($path . "comments.txt");
			$newcomment = [];
			$newcomment[] = $_POST["name"];
			$newcomment[] = $_POST["topic"];
			$newcomment[] = $_POST["message"];
			// Convert to string and add a delimiter to store in file
			$newcomment = implode(":", $newcomment);
			// Write the string to the file
			$comments_w = fopen($path . "comments.txt", 'w');

			fwrite($comments_w, $comments . "n" . $newcomment . ":" ); 
			// Show all comments
			include($path . "view_comments.php");

			?>

Figure: How the comments file looks

// View_comments.php

	<?php
//Convert to array and echo all out in a certain format within the comments div
$comments = explode(":", file_get_contents($path . "comments.txt"));
echo "<div id='comments'>";
for ($i = 0; $i < count($comments) - 1; $i += 3) {
	echo "<p>User: " . $comments[$i] . "<br> posted about: ".
	$comments[$i + 1] . "<br> and he wrote: " . $comments[$i + 2];
	echo " </p>"; 


}
echo "</div>";

?>

This application works just fine when viewed as is, but imagine if a user enters add_comment.php separately, without the file being included from the index.php. This can easily happen as the name of the service implies the file name, and this particular file name is frequently used, and the fact that add_comment.php is in the same directory facilitates the process.

Figure: Viewing add_comment.php on its own

Now, the attacker would know that we have a variable called $path and he can probably guess that we are setting the path to the comments file as there is a warning that file_get_contents(comments.txt) cannot be opened. Thus, he knows the name of the file that contains all our comments as well. Because the include is failing, he also knows the whole include_path which can also be dangerous. Also, the attacker knows another file in our directory tree (view_comments.php) so he can access it and look for some more errors. He also knows that in this file we are working with the POST values from the form, as he can view the HTML and see they are the same.

This comments form is also vulnerable to diferent code injection attacks. You can easily insert in one of the comment fields to test it out. In that way, the browsers of the users’ will execute any code that you like each time they visit the page.

A probable solution is easy: wrapping the post values in htmlspecialchars() function which converts < and > amongst others as special characters (<, >, etc.) preventing them from being interpreted as code.

$newcomment[] = htmlspecialchars($_POST["name"]);

$newcomment[] = htmlspecialchars($_POST["topic"]);

$newcomment[] = htmlspecialchars($_POST["message"]);

Solution

A simple solution to get rid of all those errors in this example is to wrap the code in add_comment.php and view_comments.php inside the following if statement:

	if (isset($path)) {	
//code here

}

In that way, the code will only execute if the files are included from index.php, presumably.

Of course, that does not handle the issue that users can post the form empty and still view the content and make the application think there is an actual comment, but that can easily be fixed and is not the issue of discussion here.

Displaying errors is good for development purposes but when the application is live and in production – always turn off display_errors from the php.ini

Exercise 10: Delimiters

We will be looking at a vulnerability similar to the one that existed in the old Poster website.

Sometimes, parameters used In the code can be abused by users even when interacting with the interface provided to them.

Open Delimiters folder from your localhost in a browser. There is a users.txt file which contains all the user data. However, access to it is forbidden from the .htaccess file:

<Files "users.txt">
Deny from all
</Files>

Try to open it using the path traversal method of the article viewer, just for practice.

Look at the different data stored there and think about what everything represents.

  1. Try to login with one of the accounts and escalate your privileges to “admin” just by communicating with the website as normal.

Spoiler

http://localhost/2/PathTraversal/?article=../Delimiters/users.txt

//The path in the GET should be valid, but you should fill the path to the index.php.

It should be clear that the “:” character is the delimiter between the different values.

You can test on the login form, but it should be clear that the first word before the first delimiter is the username, the second is the password and the third is the user’s privileges.

The code that extracts the user data one line at a time is the following:

$userlist = fopen('users.txt', 'r');
while (!feof($userlist)) {
 	$line = fgets($userlist);
 	$acc_details = explode(":", $line);
 	$username = $acc_details[0];
 	$password = $acc_details[1];
 	$access = $acc_details[2];

Then, each line is checked separately with the submitted details to check whether It matches with them:

if ($username === $_POST["name"] && $password === $_POST["pass"]) {

When it find a match, the user can be logged in.

Note that there are many better alternatives than this nowadays, such as using a database and cookies.

When logged in, you have the option to change your username or/and password.

if (isset($_POST["pass"]) && trim($_POST['pass']) !== "") {
			$userlist = str_replace /* old pass */ ($_POST["userdata-pass"],  */ new pass */$_POST['pass'], $userlist);
			echo "<em>Password changed to: " . $_POST['pass'] . "</em>
";

And to check the privileges, the script merely checks if there is a substring “admin” in the $access variable.

if (stripos($access, "admin") !== false) {
	echo "<img src="administrator.png" alt="admin" width="480" height="480" /></pre>
<h1>Howdy, admin!</h1>
<pre>
";
}

Thus, it should be clear that you can abuse this mechanism by adding the : delimiter after your password and typing admin after it when you change your password.

Solution to this vulnerability

The solution is easy and is the same as the previous exercise.

We change the code slightly:

		if (isset($_POST["usrname"]) && trim($_POST['usrname']) !== "") {
			//We remove any delimiters in the new account details an add it to a var
	  		$newacc = trim(str_replace(":", "", $_POST["usrname"]));
			//Then, we replace the old password with the $newacc variable
			$userlist = str_replace($_POST["userdata-acc"], $newacc, $userlist);
				echo "<em>Username changed to: " . $_POST['usrname'] . "</em>
";
		}

Besides sniffing and other problems, this website is again vulnerable to probability of information disclosure, as the last iteration of the while loop spills out an empty line and a PHP error would occur each time a wrong password is submitted unless display_errors is set to off.

You can do the following to avoid this as well:

if (trim($line) === "")
 		break;

Conclusion

Sometimes the solutions to vulnerabilities are really simple and do not take too much time, you just have to split the application into pieces and test them all apart from the single whole that is the application itself.