githubEdit

Testing for LFI

File Inclusion Functions (Read/Execute/Remote)

Function
Read
Execute
Remote

PHP

include() / include_once()

require() / require_once()

file_get_contents()

fopen() / file()

NodeJS

fs.readFile()

res.render()

Java

include

import

.NET

@Html.Partial()

Response.WriteFile()

include


Basic LFI Test Payloads


Directory Traversal

  • Even without the ability to upload and execute code, a Local File Inclusion vulnerability can be dangerous.

  • An attacker can still perform a Directory Traversal / Path Traversal attack using an LFI vulnerability as follows.

  • Testing

  • If you see a webpage URL look like this:

Basic Linux Test

Test for:

When the parameter is prefixed (e.g. notes=files/...): try traversing from the known path. Working patterns:


LFI Bypass Techniques

Non-Recursive Filter Bypass

If ../ is filtered but not recursively:

Null-byte and alternate encodings (PHP / app-specific)

Some apps accept null-byte or newline-style payloads (behavior varies by version and filters):

URL Encoding

Approved Path Bypass

If input must start with approved path:

Null Byte (PHP < 5.5)

Path Truncation (PHP < 5.3)


PHP Wrappers

php://filter (Read Source Code)

data:// Wrapper (RCE)

Requires allow_url_include = On:

php://input Wrapper (RCE)

expect:// Wrapper (RCE)

Requires expect extension:

LFI to RCE via Log Poisoning

  • First find the log file path and attempt to curl to it

  • Should see the evidence of your test in the user agent string logged

  • Now post php code to the log file and then visit the log file location to execute the php code you just injected

LFI to RCE via PHP Sessions

  • The LFI to RCE via PHP sessions follows the same concept of the log poisoning technique.

  • PHP sessions are files within the operating system that store temporary information. After the user logs out of the web application, the PHP session information will be deleted.

  • This technique requires enumeration to read the PHP configuration file first, and then we know where the PHP sessions files are.

  • Then, we include a PHP code into the session and finally call the file via LFI.

  • PHP stores session data in files within the system in different locations based on the configuration. The following are some of the common locations that the PHP stores in:

  • Once the attacker finds where PHP stores the session file and can control the value of their session, the attacker can use it to a chain exploit with an LFI to gain remote command execution.

  • To find the PHP session file name, PHP, by default uses the following naming scheme, sess_<SESSION_ID> where we can find the SESSION_ID using the browser and verifying cookies sent from the server.

  • To find the session ID in the browser, you can open the developer tools (SHIFT+CTRL+I), then the Application tab.

  • From the left menu, select Cookies and select the target website.

  • There is a PHPSESSID and the value. In my case, the value is vc4567al6pq7usm2cufmilkm45.

  • Therefore, the file will be as sess_vc4567al6pq7usm2cufmilkm45. Finally, we know it is stored in /tmp.

  • Now we can use the LFI to call the session file.

RCE via SSH

  • Try to ssh into the box with a PHP code as username .

  • Then include the SSH log files inside the Web Application.

LFI when the parameter has a prefix (e.g. notes=files/...)

When the vulnerable parameter is used with a fixed prefix (e.g. ?notes=files/ninevehNotes.txt), you need to traverse from that path. Probe by changing the value and watching responses:

  • Valid file – page shows content (no error).

  • Missing file – PHP warning like failed to open stream: No such file or directory (confirms include() and path handling).

  • Filtered / no include – generic message like “No Note is selected.”

What often does not work: bare path (/etc/passwd), too many ../ (e.g. “File name too long”), or dropping the prefix entirely (“No Note is selected”).

What often works: keep a prefix that still looks like a path under the allowed dir, then traverse up:

Start with fewer ../ and add more until you hit the root; too many can trigger “File name too long”. Use view-source: or curl to confirm you’re reading the real file content.

RCE via Apache logs

  • Poison the User-Agent in access logs:

  • Note: The logs will escape double quotes so use single quotes for strings in the PHP payload.

  • Then request the logs via the LFI and execute your command.


Remote File Inclusion (RFI)

Requires allow_url_include = On (except SMB on Windows).

Verify RFI

HTTP

FTP

SMB (Windows - No allow_url_include needed)


LFI with File Uploads

GIF Shell

ZIP Wrapper

Phar Wrapper

LFI to RCE via credentials files

  • This method require high privileges inside the application in order to read the sensitive files.

Windows version

  • First extract sam and system files.

  • Then extract hashes from these files samdump2 SYSTEM SAM > hashes.txt, and crack them with hashcat/john or replay them using the Pass The Hash technique.

Linux version

  • First extract /etc/shadow files.

  • Then crack the hashes inside in order to login via SSH on the machine.

  • Another way to gain SSH access to a Linux machine through LFI is by reading the private key file, id_rsa.

  • If SSH is active check which user is being used /proc/self/status and /etc/passwd and try to access /<HOME>/.ssh/id_rsa.


Automated LFI Scanning

Fuzz for Parameters

Fuzz for LFI

Fuzz for Webroot

Fuzz for Server Files

Wordlists: default-web-root-directory-linux.txt; LFI-WordList-Linuxarrow-up-right.

Reading binary files and /proc/self/fd

When the LFI returns binary (e.g. database file), save raw bytes (e.g. r.content in a script or curl -o file.bin) so the file is not corrupted. You can then open it with the right tool (e.g. sqlite3 file.bytes).

Open file descriptors: /proc/self/fd/N (e.g. 7, 9) can expose open files (e.g. SQLite DB) of the current process. Try low numbers after confirming LFI.


Bypassing Path Normalization

  • If you make a request in the browser to:

  • And you notice when you make the request the website path goes back to:

  • Use Burp Suite repeater to make the request manually

Last updated