Dot-Dot-Slash And Web App Crash

Directory traversal (or path traversal) is a vulnerability, the exploitation of which enables an attacker to read arbitrary files on an application’s server (source code, application data, backend credentials, OS files). In some cases, an attacker can write information to the files stored on the server, thus changing data and behavior of an application.

The vulnerability may arise when:

  • working with archives;
  • working with paths based on user input.

Let’s see an example to find out how it looks like in practice.

How it works?

Let’s examine an example of reading an arbitrary file through directory traversal. An application outputs an image through HTML to a page:

<img src="/loadImage?filename=218.png">

Image files are kept on the server at /var/www/images/. To load an image, the application adds the requested filename (filename value) to the specified path. In our case, the application will read the file at /var/www/images/218.png.

An attacker can use the following request to get an arbitrary file:

https://website.com/loadImage?filename=../../../etc/passwd

The application will read the file at /var/www/images/../../../etc/passwd.

../ is a step up to the next level. Three consequent steps up from /var/www/images/ will lead to the root directory. Thus, the application will read the /etc/passwd file.

NB! Sometimes a vulnerable application may encode the symbol / as %2f. Thus, the request may look like the following /..%2f..%2f..%2f..%2fetc%2fpasswd

Example of vulnerable code (1):

return file_get_contens($_GET['filename']);

This vulnerability can be found when:

  • ZIP archives are unpacked
  • dynamic content is loaded to a page
  • symlink is processed
  • PATH parameter is processed by the web server or proxying requests
  • downloading attachments stored in file systems

Another example

Directory traversal often happens when ZIP archives are processed. A ZIP archive has a certain structure. Simply put, it has file names and compressed data. When working with an archive, a programmer reads records from it one by one and unpacks it to a specified directory. Here’s an example of code that processes ZIP archives:

$zip = zip_open($uploadfile);
if ($zip) {
while ($zip_entry = zip_read($zip)) {
if (zip_entry_open($zip, $zip_entry)) {
file_put_contents(zip_entry_name($zip_entry),zip_entry_read($zip_entry));
zip_entry_close($zip_entry);
}
}
zip_close($zip);
}

Note that zip_entry_name($zip_entry) gets from the archive a file name which has to be unpacked. The vulnerability comes from the fact that the header of ZIP can contain any value because it’s a simple string, for example ../../../test. Unpacking a file with this name will result in a vulnerability.

However, this cannot be achieved with regular tools. There are utilities that allow creating an archive with the symbols that will land you in the root directory. You can read more about the ZIPSlip vulnerability following a link at the end of this article (see Useful links).

Fixes and prevention

The surefire way to prevent vulnerability is to avoid user input being transferred to the API of a file system. To prevent attacks, two security levels should be implemented:

  • User input validation before processing. It is necessary to check that user input contains only acceptable values, for example, letters and digits.
  • After validation, the application must add the input to the base directory and use API of the file system to canonicalize paths. A canonicalized path must start with a correct/expected base directory.
File file = new File(BASE_DIRECTORY, userInput);
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) {
// process file
}

Incorrect fix

Let’s consider a vulnerability in the Voyager admin panel.

public function assets(Request $request)
{
$path = str_start(str_replace(['../', './'], '',urldecode($request->path)), '/');
$path = base_path('vendor/tcg/voyager/publishable/assets'.$path);
if (File::exists($path)) {
.....
}

Look at this string:

str_replace(['../', './'], '',urldecode($request->path))

It enables sequential filtering of the file name: first, for ../, and then ./. Let’s see what happens if we submit .....///test as a file name:

  1. str_replace(['../'],'', ".....///test") = "...//test
  2. str_replace(['./'],'', "...//test") = "../test

Thus, we still managed to send a combination that results in directory traversal.

Useful links

Hacktory are professional AppSec, Red and Blue Teams developing their game-based cybersecurity educational platform https://hacktory.ai/

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store