Local File Inclusion Hacking

August 23, 2011 in Information Security, Programming, Systems Administration by trevelyn

In this video I demonstrate how to successfully find a vulnerability in PHP code which attempts to include a PHP file using include(); Vulnerable PHP code is very easy to write. In fact, it can be used to penetrate even the strongest servers, when coded by even some of the best programmers, as things like security tend to slip their minds when time is of the essence. In the video I code the PHP to include a simple text file. Sometimes you may find during a pentest that the code attempts to include yet another PHP file too, that’s okay, this method should still work. First I check for the vulnerability by testing different paths to files. A good working knowledge of UNIX is key when hacking the servers, but PHP/MySQL can still be implemented on Windows servers too. Usually, I shoot straight for anything within the /etc directory, not just the passwd file. The shadow file is where the hashes for passwd actually live and when implemented correctly, it cannot be read by users with a higher UID of 0. I try for /etc/issue which, most of the time, actually includes the server type, or branding like so:

Debian GNU/Linux 5.0 \n \l

With this knowledge, I attempt to find the web server type. This is easy and can be found using simple Firefox plugins, or using Wireshark. Once found, you can easily search Google for a few “default” directories for where the web server writes to its logs.

I usually have to do this for systems which I am not 100% familiar with. Such as RedHat Enterprise Linux, CentOS, and a few others. Each version, especially when you go to FreeBSD, Solaris, and OpenBSD, will have different directory structures. Since we are like blind pirates in a new land trying to “feel” our way around, I find it extremely useful to use Google! One could very easy write a simple Perl script to do “page comparison” just like how some SQLi applications, like SQLMap, work. This takes a small snapshot of the page and slurps that into an Array, using something like curl. And tests each default file path for the server type in which it finds. Here is a small implementation of this that I used in my pCrack Suite (which is still under development):

@files = ("../etc/config/stuff.conf", "../var/log/dmesg", "../proc/cpuinfo");
@arrayNormalPage = `curl http://website.mhm/include.php?filename=normalfile.txt`;
foreach (@file) {
$n = 0;
@arrayHackedPage = `curl http://website.mhm/include.php?filename=$_`;
# yes, we can r haz interpolation.
foreach (@arrayNormalPage) {
if $_ ne $arrayHackedPage[$n]) {
print $_ . "\n";
}$n++;
}

Now, this assumes that you are “../” away from the root directory. But you can add simple support to deduce this information from the actual URL you feed the script, like: “http://weabsite.mhm/include?file=../../../etc/motd” for instance, we have three “../” and we can see this with something like this:

$string = "http://website.mhm/include.php?filename=../../../etc/motd";
$string =~ s/http.+=//;
$string =~ s/([a-zA-Z0-9_-]+\/)+[A-Za-z0-9._-]*//;

This would leave us with the variable $string equal to “../../../” and we can just start brute forcing the files from there. Each file which returns a different result from what the page normally looks like will be printed on the screen. Now, in my implementation I made this dump the file name into an array which it then returns all positive entries after the script is ran. One thing you may want to keep in mind while trying to find the vulnerability, is that some coders will append a “.php” or “.txt” or whatever to the end of the file. This can be easily detected by the attacker who will see the error displayed on the page stating something like:

ERROR: filename "../../../etc/resolv.conf.php" No such File or Directory.

This can also be easily thwarted by an attacker by simply including a null byte at the end of the URL, like so:

http://website.mhm/include.php?filename=../../../etc/debian_version%00

Back to hacking: we could attempt to read a few config files in /etc/init.d/ as well in this case. I search for logs so that I can make the server run my own code. Since the actual file I am calling with the browser is a PHP file include.php it will render HTML and execute any <?php code here ?> lines. Also, since the web server usually is limited in to which files it can write, you know that it must be keeping a log somewhere, so what better place to start?
Here is a sample line from my lighttpd log file:


::ffff:00.00.00.00 weaknetlabs.com - [14/Aug/2011:06:25:20 +0000] "GET /index.php HTTP/1.1" 200 8164 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.9.2.18) Gecko/20110614 Firefox/3.6.18"

This shows that it is logging the User Agent which is passed by my browser during the HTTP request. With a simple tool called User Agent Switcher, which is a Firefox plugin, I can make this line say whatever I’d like it to! This can actually be done in many ways, even by manipulating the packets with a Libpcap based tool, such as Ettercap-ng. I then change my User Agent to include PHP code which gets executed by the server when the access_log file gets read.

This will allow me to exec() anything I’d like as the user “www-data,” which is somewhat limited, but not bad. I then usually try to ‘ls -l’ the directory of the web root and see if the server has been configured to allow www-data to write to any of the directories. It usually is when running software like WordPress, Drupal, or any other CMS in which users are allowed to upload files. Once found I then upload a PHP “shell” script which will easily allow me to type commands sent directly to the server. These scripts can be found anywhere using Google, or easily written using PHP/AJAX.
Once the shell is up, I am basically logged into the UNIX machine as www-data. I usually have to edit the $PATH variable to include more binaries, but from there, I can then wget local exploit files, compile them and run them. Or easily read any other PHP files which may include password, username, and host lines to connect to the database:

define('DB_PASSWORD', 'password');
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
mysql_connect("localhost", "admin", "1admin") or die(mysql_error());

Once you have access to the DB, you can check to see if the MySQL-client package is installed, as in our case – Debian 5.0
If so, connect with mysql -u <username> -p and then type the password when asked.

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.


mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
+--------------------+
2 rows in set (0.02 sec)


mysql>

From here, it is obviously a good thing to have good SQL skills! SQL is an easy language that’s a lot of fun and allows you to read, write, and change databases. During a pentest, it’s probably not a good thing to drop tables or delete rows, fields, or even truncate tables!