SANS Penetration Testing

Getting MOAR Value out of PHP Local File Include Vulnerabilities

By Jeff McJunkin


Wouldn't web application penetration testing be easier if you could look at the source code? Well, when looking to expand my web app pen testing skills, my good friend and co-worker, Josh Wright, mentioned a specific new twist for Local File Include vulnerabilities on PHP-based web servers: PHP wrappers.

PHP wrappers allow us to make Local File Includes far more useful. The PHP interpreter can handle many things as streams of data, such as local files, remote URL's, and even remote SSH systems! What's most interesting today, though, is accessing "various I/O streams" through the php:// wrapper. With php://, we can read the Standard Input, Standard Output, and Standard Error of the PHP interpreter itself. For example, with php://stdin, we can read the raw input a user sends, which can be useful if the PHP interpreter adds some formatting and such that can get in our way. This often comes up with JSON being sent to a PHP interpreter, for example.

The most interesting wrapper, though, is the most meta of them all — the php://filter wrapper, which allows us to apply one or more file transforms to a file input or output. What kinds of filters, you ask? All sorts of kinds, I reply!

$ php -a
Interactive mode enabled

php > $streamlist = stream_get_filters();
php > print_r($streamlist);
[0] => zlib.*
[1] => string.rot13
[2] => string.toupper
[3] => string.tolower
[4] => string.strip_tags
[5] => convert.*
[6] => consumed
[7] => dechunk
[8] => convert.iconv.*

Honestly, I'm not sure why I'd need to do ROT13 on a stream of text as part of a real application, but it's not nearly as interesting as the filters underneath item #4, convert — specifically convert.base64-encode. How do we make it work? The syntax is a bit odd, but luckily a pre-canned line will do for 99% of cases:

$ echo "Something secret" > secret.txt
$ php -a
php > include("php://filter/base64-encode/resource=secret.txt");
Something secret


Good point, Philosoraptor! In this example, I could've just as well done this:

php > include("secret.txt");
Something secretSomething secret

So why use php://filter at all? Well, oftentimes a file extension is added on the server-side code as follows:

include($_GET["page"] . ".php");

And imagine I have another page, index.php, that looks like this:

print("Some index page or something\n");

You've probably seen URL's like the following: http://localhost/ex1.php?page=index. That URL will load index.php (the ".php" is added server-side, remember) and include it in the HTTP response as follows:

$ curl "http://localhost/ex1.php?page=index"
Some index page or something

However, we can't see the source to either ex1.php or index.php, because the PHP interpreter will be interpreting that code, not merely displaying it. With the magic of the base64-encode PHP filter, though, the PHP interpreter will not interpret the resource as code. This allows us to see the original server-side PHP code content! Let's give it a shot:

$ curl -s "http://localhost/ex1.php?page=php://filter/convert.base64-encode/resource=index"
$ curl -s "http://localhost/ex1.php?page=php://filter/convert.base64-encode/resource=index" | base64 -d
print("Some index page or something\n");

Great! The ".php" extension is still added on the server side, but we've managed to get something very useful anyway. Now we can examine the PHP for SQL injection, code injection, secrets like database connection information, etc.

Have fun hacking all the things!

-Jeff McJunkin

I am teaching SEC560: Network Penetration Testing and Ethical Hacking in Chicago at the SANS Automotive Summit & Training event in May 2018.

SANS Pen Test Austin 2018 - Training Event:


  • Choose from 12 world-class training courses w/ our best instructors!
  • Play in (3) Nights of NetWars
  • Join a team as you hack/defend SANS CyberCity
  • Enjoy a special night of networking and fun for all attendees
  • Earn up to (5) SANS Pen Test Challenge Coins during Coin-A-Palooza
  • March 19 - 24, 2018 - Austin, TX
  • Learn more:

Post a Comment


* Indicates a required field.