Why does PHP include statement treat contents following '?/../' as a path from current directory?

160 Views Asked by At

Given a script temp.php in current user directory ~:

#!/usr/bin/php
<?php
$p=$argv[$argc-1];
$a=file_exists($p);
echo "File exists?". $a ."\n";
include $p
?>

Using the script above, one can pass a filename as argument and retrieve its text content. I don't think it has any practical usages other than illustrating my problem. However, some CTF solutions leverage a malformed path string to include any file. It seems that no matter what precedes the string '?/../' or whether it exists, the following content will be treated as a path from the current directory.

cd ~
# Make the script executable
chmod +x temp.php
# Create a temporary file
echo empty > empty

./temp.php empty # Output "File exists?1" and "empty". It's fine and expected.
./temp.php 'test?/../empty' # Ouput 'empty' and a newline
./temp.php 'test?/../../../../../etc/hosts' # Output contents in /etc/hosts

I can't find related information documented on PHP page of the include statement. Does anyone have knowledge on this topic?

2020/04/18 21:06 Update. My PHP version is 7.2.24 on Ubuntu 18.04.

2020/04/18 21:20 Update. Orz maybe I figure out that it was the .. causing the problem, not the question mark ?. PHP include seems to collapse consecutive ..s. I am still wondering if this behavior was properly documented somewhere?

ubuntu@VM-0-5-ubuntu:~$ ./temp.php 'test/../empty'
File exists?
empty
ubuntu@VM-0-5-ubuntu:~$ ./temp.php 'test/../../ubuntu/empty'
File exists?
empty
ubuntu@VM-0-5-ubuntu:~$ 
0

There are 0 best solutions below