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.

High-value LFI targets: SSH keys and /proc/self/fd/*

SSH keys: Try both key types per user (from /etc/passwd): /home/USER/.ssh/id_rsa, /home/USER/.ssh/id_ed25519, and .../authorized_keys. Example path that yielded a usable key: /home/trivia/.ssh/id_ed25519. Passphrase-protected keys: ssh2john keyfile > key.hash then john --wordlist=/usr/share/wordlists/rockyou.txt key.hash.

Open file descriptors: /proc/self/fd/N (e.g. 7, 9) — the current process’s open files. Often one fd is the application’s SQLite DB; reading it via LFI can expose cama_users and other tables. Try /proc/self/fd/7, /proc/self/fd/9, etc.

Reading binary files (e.g. SQLite DB) via LFI

When the LFI returns the raw file content (not wrapped in HTML), binary files must be fetched as bytes, not as text. Some exploits (e.g. Camaleon CMS CVE-2024-46987) print r.text by default — for SQLite or other binaries you must save r.content to a file. Save the response to disk and open with sqlite3 out.bytes; then query tables (e.g. cama_users for bcrypt hashes). See Camaleon CMS for the full flow.

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.

  • SSH keys via LFI: Target private keys and authorized keys for users from /etc/passwd:

    • /<HOME>/.ssh/id_rsa — RSA private key

    • /<HOME>/.ssh/id_ed25519 — Ed25519 private key (e.g. /home/USER/.ssh/id_ed25519)

    • /<HOME>/.ssh/authorized_keys — authorized keys for passwordless login


Automated LFI Scanning

Fuzz for Parameters

Fuzz for LFI

Fuzz for Webroot

Fuzz for Server Files

Wordlists and references

Purpose
Wordlist / link

LFI path fuzzing

/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt

Web root / server paths (Linux)

/usr/share/seclists/Discovery/Web-Content/default-web-root-directory-linux.txt

Parameter names

/usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt

Seclists: https://github.com/danielmiessler/SecLists (Fuzzing/LFI, Discovery/Web-Content).

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