Web Application Security for Beginners · lesson 2 · 2026-08-24 · updated 2026-10-12
File Inclusion & Path Traversal — Web Application Security for Beginners (LFI/RFI)
Before you start
- DVWA (Damn Vulnerable Web App, a deliberately vulnerable practice app) from the
vulnerables/web-dvwaDocker image, bound to 127.0.0.1:8080, security levellow. - Logged in as
admin/passwordwith the session cookie saved tolab/cookies.txt(curl -b lab/cookies.txt ...), so the on-screen commands stay short. curl(preinstalled on Kali).- Only test systems you own or are authorized to test.
Welcome back to The Exploit HQ, and to part two of our beginner series on web application security. Just like last time, you need zero experience to follow along.
In part one we changed a number and saw a stranger’s shopping cart. Today we do something that feels almost magical, and a little scary. We are going to make a website hand us files from deep inside its own computer, files it was never meant to show anyone.
First, the plain words. Many web pages are not one single file. To build one page, the app quietly loads several smaller files and stitches them together.
What is file inclusion?
- A web page is often built from many smaller files
- The app ‘includes’ a file to show it as part of the page
- Often the file’s name comes from the web address (?page=…)
- If the app trusts that name blindly — you pick the file
The app decides which file to load, and very often the file’s name is sitting right there in the web address, after the letters p-a-g-e and an equals sign. That little setting is meant to load the app’s own pages. But if the app trusts whatever name we put there, then we, the visitor, get to choose the file. And that is the whole problem.
This flaw comes in a few flavours, and the names sound scarier than they are.
LFI, RFI, and path traversal
- L F I — Local File Inclusion: read a file already ON the server
- R F I — Remote File Inclusion: load a file from the internet (worst case)
- Path traversal — use ../ to climb up out of the allowed folder
- ../ just means ‘go up one folder’, like a back button for folders
L F I, local file inclusion, means we read a file that already lives on that same computer. R F I, remote file inclusion, is the nastier cousin, where the app loads a file from somewhere else on the internet, even one the attacker controls. And path traversal is the trick we use to move around the disk. Those two little dots and a slash, dot dot slash, simply mean, go up one folder, exactly like the back button in a file browser. Do it enough times and you reach the very top of the disk.
You might be thinking, so what, it is just a file. Here is why it matters.
Why should you care?
- Read the server’s list of user accounts (etc passwd)
- Read config files — often with database passwords inside
- Read the app’s own source code, and its secrets
- With R F I, the server can be tricked into running attacker code
The same trick can read the server’s list of user accounts, its configuration files, which very often contain database passwords in plain text, and the app’s own private source code. And in the worst case, remote file inclusion, the server can be fooled into running an attacker’s code, which is a complete takeover. This family is tracked by the security world as path traversal, CWE twenty two, and file inclusion, CWE ninety eight, and it lands inside O-WASP’s number one category, broken access control, the very same top category from part one.
For the demo we will use our own local practice app, and just three simple tools.
The tools we’ll use
- curl — sends a web request from the terminal (a browser with no window)
- cookies.txt — our saved logged-in session (like a wristband)
- grep — keeps only the lines we care about from a big reply
curl is that small program from part one that sends a web request straight from the terminal, like a browser with no window. cookies dot txt is a file holding our logged-in session for our own practice app, think of it as the wristband from part one that proves we are allowed in. And grep is a tiny helper that reads a big pile of text and keeps only the lines we ask for, so the answer is easy to read.
Our practice target is DVWA, the Damn Vulnerable Web Application, running only on our own machine. It has a page whose whole job is file inclusion. Let’s break down that page’s address before we touch it.
The vulnerable page
http://127.0.0.1:8080/vulnerabilities/fi/?page=include.php
| Part | What it does |
|---|---|
http://127.0.0.1:8080 |
our OWN computer — 127.0.0.1 always means ‘this machine’ |
/vulnerabilities/fi/ |
the File Inclusion practice page (fi = file inclusion) |
?page=include.php |
which file to load — normally one of the app’s OWN pages |
?page=/etc/passwd |
…but nothing stops us naming a DIFFERENT file |
So one-two-seven dot zero dot zero dot one is our own computer, nobody else’s. The f-i page is built for file inclusion practice. And that page equals part is where the app expects the name of one of its own pages. The flaw is that it never checks, so we can quietly ask for a completely different file instead.
Let’s break down the actual command we’ll run, piece by piece.
Step 1 — ask for a file we shouldn’t get
curl -s -b cookies.txt ".../fi/?page=/etc/passwd" | grep -E "^root:|^www-data:|^mysql:|^daemon:"
| Part | What it does |
|---|---|
curl -s |
send a web request quietly, with no progress bar |
-b cookies.txt |
send our saved login session — the wristband — so the app lets us in |
?page=/etc/passwd |
instead of an app page, ask for the system’s user-account file |
| grep -E "^root:…" |
from the reply, keep only the real account lines so it’s readable |
In plain terms, we prove we are logged in with our wristband, then we ask the page to load the file called etc passwd, which is the computer’s own list of user accounts. Then grep tidies the answer down to the interesting lines. Let’s open the terminal and type it together.
Type it with me, slowly.
curl -s -b cookies.txt "http://127.0.0.1:8080/vulnerabilities/fi/?page=/etc/passwd" | grep -E "^root:|^www-data:|^mysql:|^daemon:"
Look at that. Those are real user accounts from inside the server. root is the all-powerful administrator. www dash data is the account that runs the website. mysql is the database account. This is the etc passwd file, a file that is supposed to stay on the server, out of sight. The page was only meant to load its own pages, yet it happily read a private system file just because we asked.
Now, in that first command we handed over a full, exact path. But often an app only lets us pick from its own folder. So how do we escape that folder? We climb, using dot dot slash.
Step 2 — climb out with path traversal
curl -s -b cookies.txt ".../fi/?page=../../../../../../etc/passwd" | grep -E "^root:|^www-data:|^mysql:|^daemon:"
| Part | What it does |
|---|---|
?page=../../../../../../ |
each ../ climbs UP one folder — repeat until we reach the disk’s top |
etc/passwd |
from the top, walk back down into the same secret file |
the rest |
identical to before — same wristband, same tidy-up with grep |
Each dot dot slash steps us up one folder, toward the top of the disk. We add a few extra, just to be sure we reach the very top, and then we walk down into etc passwd. Extra climbs at the top do no harm, you simply cannot go higher than the top.
Here we go.
curl -s -b cookies.txt "http://127.0.0.1:8080/vulnerabilities/fi/?page=../../../../../../etc/passwd" | grep -E "^root:|^www-data:|^mysql:|^daemon:"
The exact same secret file, but this time we reached it by climbing folders with dot dot slash, instead of typing the full path. That climbing move is what the word path traversal means. And remember the scariest cousin, remote file inclusion. If the server allowed it, that page equals part could point at a file on the attacker’s own website, and the server would fetch and run it. We will not demo that one, because it needs a second machine, but it is the very same root cause, an app trusting a file name it should never trust.
So how do you find this flaw, and how is it fixed?
Find it, and fix it
- FIND: put a path in the page value — try ../ or a full path like etc passwd
- FIND: if a file you shouldn’t see comes back, it’s vulnerable
- FIX: never pass a user’s text straight into an ‘include’ or file open
- FIX: use an allow-list of permitted pages; turn OFF remote includes
To find it, you do exactly what we did. Put a path where the app expects a page name, try climbing with dot dot slash or a full path, and see if a file you should not get comes back. To fix it, the golden rule is, never feed a visitor’s text straight into a command that opens or includes a file. Instead, keep a short allow list of the exact pages that are permitted, reject everything else, and switch off the setting that lets the app load files from the internet. That closes the whole hole.
And one rule, every single time.
Before you go
- Only test apps you own or have written permission to test
- D V W A is built for exactly this — run it locally to practice
- Coming next — Part 3: Information Disclosure
Only ever test apps you own, or have clear written permission to test. Our DVWA runs only on our own machine, and it was built for exactly this kind of safe, legal practice. In part three, we will look at information disclosure, all the little ways an app leaks secrets it never meant to. Thanks for learning with The Exploit HQ, and I’ll see you there.