1

I'm trying to understand how null byte injection attacks in PHP code used to work before it was patched in PHP 5.3.4. I have this sample HTML page that is a somewhat modified version of what's shown here:

<?php
var_dump($_GET);
echo '<br>';
$file = $_GET['file'];
$path = '/var/www/' . $file . '.php';
echo $path;
if (file_exists($path)) {
    echo "file exists!";
    include $path;
}
?>

This code is running on an Ubuntu 12.04 system running PHP 5.2.17. I would expect a URL like http://localhost/?file=../../../etc/passwd%00 to work, but instead I see \0 printed literally in the string:

array(1) { ["file"]=> string(21) "../../../etc/passwd\0" }
/var/www/../../../etc/passwd\0.php

What am I doing wrong? How can I get null byte injection attacks working on my local machine?

gsgx
  • 1,225
  • 2
  • 12
  • 13
  • While I haven't went back to test this, I read later on that the magic_quotes_gpc escapes the null byte. Maybe this was enabled? – gsgx Oct 08 '15 at 04:04

1 Answers1

1

The echo output with the \0 included is supposed to happen, nothing to worry. In order to understand the attack, you have to know how C and PHP store strings.

C

In C, a string is simply a a pointer to a memory address where the pointer points to the start of the string, and the \0 character indicates the end. No length is stored, all code that reads strings reads until the \0 character is reached. A C string can be as long as it wants to be.

PHP

In PHP, a string consists of two parts: one integer and one array. The integer denotes the length of the string and the array, the array contains the string itself. All String handling code in PHP reads that integer and knows how long the string is. It ignores the \0 character. The integer has an upper limit, and so does the length of the string.

The attack

The attack bases on this different string handling. So as you can include a \0 character into the PHP string, printing the string will include the \0 character. So your output will be /var/www/../../../etc/passwd\0.php. The magic happens the moment PHP gives that string to C: C interprets the \0 character as string termination and thinks it got /var/www/../../../etc/passwd. And this file of course exists.

The fix

From the release notes:

Paths with NULL in them (foo\0bar.txt) are now considered as invalid. (Rasmus)

The fix mostly consists of adding if-clauses, comparing the string length with the c method (left) and the php method (right):

if (strlen(Z_STRVAL_PP(file)) != Z_STRLEN_PP(file)) {
    RETURN_FALSE;
}
user10008
  • 4,315
  • 21
  • 33
  • But "file exists" was never printed? – gsgx Aug 29 '14 at 17:34
  • what's the output of `dpkg -s php5 | grep 'Version'`? – user10008 Aug 29 '14 at 17:42
  • I'm asking, as ubuntu may have backported the patch... – user10008 Aug 29 '14 at 19:49
  • `Version: 1:5.2.17.dfsg.1-0jblgf1~lucid`. The repositories only have 5.3.10, so I had to install it by downloading the deb files [here](https://launchpad.net/~jblgf0/+archive/ubuntu/ppa/+packages). Also, the newer versions (at least php 5.5) with the patch don't show `\0`, they simply skip that byte. – gsgx Aug 29 '14 at 22:48
  • Unfortunately I can't tell whether a backport has been made from the changelog... Anyway it's very unlikely, as the bug is not this severe. – user10008 Aug 30 '14 at 22:31