2

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:~$ 
kayochin
  • 431
  • 6
  • 11
  • Might be a linux thing, cause `include 'test?/../include.php';` fails as it should on my Windows setup (include.php being in the same folder as my script). – Jeto Apr 18 '20 at 11:39
  • @Jeto I think that it has something to do with PHP. Strings like `'test?/../empty'` can not be resolved to a valid path. `ls 'docker?/../empty'` complains "No such file or directory", while PHP does include its content. – kayochin Apr 18 '20 at 12:01
  • What PHP version? What does `file_exists` say? – Jeto Apr 18 '20 at 12:15
  • 1
    I suspect this will differ dramatically based on operating system and PHP version (and ini settings, etc etc), but I can reproduce something similar without that pattern by running `php -r 'include "this/does/not/exist/../../../../../../etc/passwd";'` from the CLI. Using [`stream_resolve_include_path`](https://www.php.net/manual/en/function.stream-resolve-include-path.php) is supposed to help rationalise that kind of thing, but for me it's returning false, despite the include itself working fine. This is on Ubuntu 18 / PHP 7.2. – iainn Apr 18 '20 at 12:22
  • @Jeto `file_exists` returns nothing when given the malformed path. It does return a value of `1` if the path is valid. By the way, I just updated the script in question containing a call to `file_exists`. – kayochin Apr 18 '20 at 13:16

0 Answers0