When dealing with PHP files, there’s a concept called PHP Wrappers. A wrapper is a kind of envelope that tells the Stream (sequence, request, input/output data) how to act.

This PHP feature is very useful in attacks like LFI and XXE, thanks to this, we can obtain some advantages that we wouldn’t have otherwise.

The wrapper concept will become clearer when we see it now.

Index:

php://filter

The filter wrapper allows us to encode the file we specify, this is very useful, as it allows us to read PHP files that otherwise the browser would simply interpret directly.

For example, we have the following file:

PHP code of secret.php file with password in comment

As we can see, it has a password in a comment. But if we access the file from the web:

Browser showing only the output of interpreted PHP code

We only see the output of the interpreted code 😥. However, using the filter wrapper, we’ll be able to read the complete PHP file.

To test the wrapper, I’ve created an LFI in an index.php file. So, in this LFI, the payload we’ll introduce to use the wrapper and read the secret.php file will be the following:

php://filter/convert.base64-encode/resource=<file>

Using php://filter to obtain PHP file encoded in base64

This way, we’re reading the secret.php file but in base64, so if we decode this output:

Base64 decoding revealing complete PHP code with password

We obtain the complete file. A curious detail about wrappers is that we can concatenate several through the use of a pipe | or a slash /. Example:

Concatenation of wrappers using pipe and slash

Identical result when concatenating multiple wrappers

And we get exactly the same result.

Besides being able to encode in base64, we can apply ROT13 with the following string:

php://filter/read=string.rot13/resource=<file>

Although this specific one doesn’t work for reading PHP files:

ROT13 wrapper doesn't work for reading PHP files

But it does apply for other types of files:

ROT13 wrapper working correctly with text files

In conclusion, regarding this wrapper, we have the following two payloads:

php://filter/convert.base64-encode/resource=<file>
php://filter/read=string.rot13/resource=<file>

zip://

The zip wrapper allows us to execute a PHP file that we’ve placed inside a zip file. It’s not even necessary for the zip file to have a zip extension, it can have any extension.

This wrapper isn’t installed by default, but it can be installed with the following command:

sudo apt install phpX.Y-zip

Where X and Y are the PHP version we have installed or the one we want to install this feature for.

Example of webshell execution through this wrapper:

Command execution using zip wrapper with webshell

Payload:

zip://<zip file>%23<php file>

In , if it’s not in the current directory, you would specify the directory where the file is located and that’s it.

Note: in case the PHP file is a webshell or expects some parameter, it would be added with an ampersand as we see in the following image.

Using ampersand to pass parameters to zip wrapper

And even changing the zip extension, it will still work:

ZIP file renamed with jpg extension

Zip wrapper working with file renamed as jpg

data://

The data wrapper allows us to include external data, including PHP code. This wrapper only works if the allow_url_include option is enabled in the PHP configuration (the equivalent option to a Remote File Inclusion).

Executing PHP code with this wrapper is quite simple, we can do it in two ways:

  • In plain text
  • In base64

In plain text, we simply would have to use the following payload:

data:text/plain,<PHP code>

Example:

Executing PHP code in plain text using data wrapper

To do it using base64, we simply would have to encode the PHP code:

Encoding PHP code in base64

And place it in the wrapper like this:

data://text/plain;base64,<PHP code in base64>
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOyA/Pgo=

This way, since we’re defining a parameter to execute commands, the payload to for example execute the id command would be:

data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUW2NtZF0pOyA/Pgo=&cmd=id

Example:

Executing id command using data wrapper with base64

php://input

This wrapper is similar to the one above (data). It can be used to include PHP code. Its requirement, like the data wrapper, is that the allow_url_include option in the PHP configuration must be enabled.

With this done, commands could be executed by sending the PHP code in the data of a POST request. Example:

curl -s -X POST -d '<PHP code>' 'http://example.com/index.php?file=php://input'

Command execution using php://input wrapper with POST request

In this case, we can see the command output in the response.

expect://

The expect wrapper isn’t installed by default, but in case it is, it allows directly executing commands in the following way:

expect://<command>

This happens because this wrapper gives access to a PTY (pseudo-teletype), which in UNIX basically refers to a terminal. It gives access to STDIN, STDOUT as well as STDERR.

PHP Wrappers Conclusion

As we’ve been able to see, this PHP feature is very useful on many occasions, as it can help us achieve actions that we couldn’t otherwise. It’s quite useful to use them when we’re facing vulnerabilities like Local File Inclusion (LFI) or XML External Entity (XXE), or really in any case where we see we have the ability to use them.

References