RobHoward

Yet another tech blog. Yaaay.

Not-so-obvious Security Holes: include() and NUL.

Consider the following code:

  if (!empty($_GET['template']) && strpos('/', $_GET['template']) === FALSE) {
    include dirname(__FILE__).'/'.$_GET['template'].'.html';
  }

Save it in display.php, and access it using a URL that looks like:
http://example.com/display.php?template=blue

At first glance, it only includes files in the current directory with the extension “.html”. This isn’t true; access the URL with the following instead and see what happens:
http://example.com/display.php?template=display.php%00

Ouch.
Not only does that put the code in a recursive loop, it also shows that it can go and run any other files in the directory.

PHP is a bit silly. By default, if include() encounters a NUL byte (encoded as %00 above, commonly represented as \0) in the string passed to it, it just uses everything up to the NUL byte as the filename. (Our second example above takes the file path /path/to/webroot/display.php\0html and ignores \0html, include()-ing the display.php file instead.)

In short: if you need to include a file use a whitelist. Here’s a really simple example of the above code modified to make use of a whitelist instead:

  if (!empty($_GET['template'])) {
    $path = dirname(__FILE__);
    switch ($_GET['template']) {
      case 'red':
      case 'blue':
        include $path.'/'.$_GET['template'].'.html';
        break;
      default:
        include $path.'/default.html';
    }
  }

(And make sure your server is running with the Suhosin PHP-hardening patch to close this hole, along with a bunch of other non-obvious ones.)

Leave a Reply