0

I have received someone else's code for a system that shows folders with photos in them in your browser.

For example, this is a possible url on the site:

gallery.php?action=view&folder=Cars

At the moment, you can replace "Cars" with ../../../../../, which will happily show the Linux root folder. Luckily, the website is offline at the moment.

I tried using realpath to fix this. Here's what I have so far:

case 'view' :
$name = realpath(dirname(__FILE__) . "/Content/Gallery/" . $_GET['folder']);

echo $name;

$data = $file->getPictures($name);
require 'Views/Gallery.view.php';
break;

I have added echo on the third line to see what the URL would be. In the url above, everything is fine, and echo outputs this:

/var/www/Content/Gallery/Cars 

So far, so good. However, if I enter /../../../../../../../../ instead of "Cars", $name becomes / and the page still shows the root folder. English isn't my first language, so I could be misunderstanding how realpath works or what it does. From what I understood, it removes any instance of ../ from a given URL.

Can someone please tell me what I'm doing wrong?

yesman
  • 5,531
  • 9
  • 39
  • 87
  • 1
    possible duplicate of [Preventing Directory Traversal in PHP but allowing paths](http://stackoverflow.com/questions/4205141/preventing-directory-traversal-in-php-but-allowing-paths) – Patrick Q Jan 08 '14 at 20:15
  • 2
    This is why PHP gets a bad rap :\ – Mike B Jan 08 '14 at 20:15
  • @Mike why? `realpath()` was not designed to fight directory traversal attacks; nor does it claim to do so – Pekka Jan 08 '14 at 20:16
  • @Pekka웃 People keep perpetuating these terrible scripts. I'm surprised there's no `mysql_query($_GET['sql']);` here... – Mike B Jan 08 '14 at 20:16
  • @Mike yeah, that's true. – Pekka Jan 08 '14 at 20:17

2 Answers2

6

realpath() alone is not sufficient to fight directory traversal.

What you describe is exaclty what it's designed to do: it translates relative things like ../ into the actual directory path.

However, for security, you can take a realpath() ed path and see whether it still is a child of a safe base path. If it isn't, somebody tried to sneak into a directory they have no business being in:

 $name = realpath(dirname(__FILE__) . "/Content/Gallery/" . $_GET['folder']);

 // The safe root directory. 
 $safe_root = dirname(__FILE__) . "/Content/Gallery/";

 // Check whether realpath() result is still inside /Content/Gallery
 if (substr($name, 0, strlen($safe_root)) != $safe_root) 
   die ("Possible directory traversal attack");
Pekka
  • 418,526
  • 129
  • 929
  • 1,058
1

From what I understood, it removes any instance of ../ from a given URL.

No, that's not what it does. It's not for URLs, it's for paths. It simply converts a path and expands ../ into the right folder. It doesn't remove them, it resolves them - meaning it calculates what the ../ stands for and alters the path to have that.

It also changes / to \ on Windows.

realpath

realpath() expands all symbolic links and resolves references to '/./', '/../' and extra '/' characters in the input path and returns the canonicalized absolute pathname.

Jessica
  • 7,019
  • 26
  • 38
  • Thanks, I was confused by the example on that page. On there, an example outputs "echo realpath('./../../etc/passwd');" as "/etc/passwd". How do I stop directory traversal then? – yesman Jan 08 '14 at 20:15
  • Read the example again. Before that it moves to a directory. Relative to that directory, `'./../../etc/passwd'` can be converted into `/etc/passwd` – Jessica Jan 08 '14 at 20:15