Corridor
A small literary press publishes their quarterly journal online. Every piece — story, essay, poem — lives on disk as a page fragment. When you click through to read one, the server fetches that fragment and assembles the page. The path handling is… not careful.

Room Description
There is a new feature on WebVerse called Foundational labs, there are meant to be easier than Easy and build some basic web exploitation skills.
This is information directly grabbed from the main page and description of the lab.
https://dashboard.webverselabs-pro.com/foundational-labs/corridor
Ridgeline Press is a three-person independent literary press in the Pacific Northwest. They publish a quarterly journal of fiction, essays, and poetry, and their site is a static-as-it-gets PHP app that loads each piece from a file on disk. No CMS, no database — just a folder of HTML fragments and one unlucky readfile call. Read the site. Find what shouldn't be there.Synopsis
Find the file the editors forgot to move.
What is Corridor
A beginner-friendly PHP + Apache literary-press site that loads each published piece from disk via a user-controlled slug. One-page LFI, no filters, no null-byte tricks — just `../`.
Who is Corridor for?
Newcomers comfortable with one injection lab who are ready to learn that input going into a filesystem path is just as dangerous as input going into SQL.
Skills / Knowledge
- Reading robots.txt as a discovery aid
- Recognising the shape of include / readfile patterns in URL parameters
- Basic `../` path traversal
What will you gain?
- Recognise that a URL parameter feeding into a filesystem path is a candidate for traversal.
- Read a site's robots.txt and think of it as a list of 'interesting things the owner didn't want you to notice'.
- Distinguish between what HTTP blocks and what PHP can still read — they're not the same boundary.
Initial Analysis
Okay, let's try to take a look at this lab from scratch. We are given an IP address, let's put it in our browser to see where it tries to send us.

We can't resolve this domain, to fix this, we need to add the IP address given to us, and the domain name we're attempting to resolve to /etc/hosts.
On Windows the file is located at C:\Windows\System32\drivers\etc\hosts , on Linux it's just /etc/hosts. If you have issues editing the file on Windows, just create a new text file wherever, edit it there, then copy and paste it to the location to override the old one. To do so, you do need Local Admin or sudo permissions.
After doing so, we can try to refresh and we can see that we now have access to the web app.

From the frontend immediately we can see which endpoints we have available to us:
<nav class="rp-nav">
<a href="/">Current Issue</a>
<a href="/archive.php">Archive</a>
<a href="/about.php">About</a>
<a href="/submit.php">Submit</a>
</nav>
as well as a /piece.php endpoint that is used to gather the articles I assume.
<div class="rp-pieces">
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-fiction">Fiction</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=tidewater-by-anna-mills.html">Tidewater</a>
</h3>
<div class="rp-piece-byline">by Anna Mills</div>
<p class="rp-piece-excerpt">The ferry was late again, and the tide was coming in fast enough to matter.</p>
<a class="rp-piece-read" href="/piece.php?slug=tidewater-by-anna-mills.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-essay">Essay</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=the-green-door-by-james-burrows.html">The Green Door</a>
</h3>
<div class="rp-piece-byline">by James Burrows</div>
<p class="rp-piece-excerpt">My grandfather kept a door in the shed that led nowhere. This is the story of why.</p>
<a class="rp-piece-read" href="/piece.php?slug=the-green-door-by-james-burrows.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-poetry">Poetry</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=low-tide-in-october-by-claire-nishimoto.html">Low Tide in October</a>
</h3>
<div class="rp-piece-byline">by Claire Nishimoto</div>
<p class="rp-piece-excerpt">Three poems on the rhythm of the coast and the small things it leaves behind.</p>
<a class="rp-piece-read" href="/piece.php?slug=low-tide-in-october-by-claire-nishimoto.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-essay">Essay</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=field-guide-to-saltgrass-by-david-okonkwo.html">A Field Guide to Saltgrass</a>
</h3>
<div class="rp-piece-byline">by David Okonkwo</div>
<p class="rp-piece-excerpt">On a plant nobody looks at, and what it quietly holds together.</p>
<a class="rp-piece-read" href="/piece.php?slug=field-guide-to-saltgrass-by-david-okonkwo.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-fiction">Fiction</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=two-letters-by-marta-escribano.html">Two Letters</a>
</h3>
<div class="rp-piece-byline">by Marta Escribano</div>
<p class="rp-piece-excerpt">One was meant to be sent. The other was meant to be kept.</p>
<a class="rp-piece-read" href="/piece.php?slug=two-letters-by-marta-escribano.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-essay">Essay</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=what-the-ferry-taught-me-by-ben-kimura.html">What the Ferry Taught Me</a>
</h3>
<div class="rp-piece-byline">by Ben Kimura</div>
<p class="rp-piece-excerpt">Forty-one crossings, one schedule, and a quiet argument with my own hurry.</p>
<a class="rp-piece-read" href="/piece.php?slug=what-the-ferry-taught-me-by-ben-kimura.html">Read the piece →</a>
</article>
</div>
</section>
From the Response headers we can see that the application is PHP based.
HTTP/1.1 200 OK
Server: nginx/1.29.8
Date: Sat, 25 Apr 2026 16:22:54 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 5600
Connection: keep-alive
X-Powered-By: PHP/8.2.30
Vary: Accept-Encoding
Finding the bug
We need to get familiar with the web app, so let's see the archive and whether it has anything that we can call.
/archive.php

Seems to be nothing here, it's just an ordered list.
<section class="rp-archive">
<ol class="rp-archive-list">
<li class="rp-archive-row">
<span class="rp-archive-num">Issue XIV</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Autumn 2026</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-note">Current issue</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue XIII</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Summer 2026</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-note">Pacific Waters folio</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue XII</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Spring 2026</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-note">New voices</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue XI</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Winter 2025</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-note">Fireside essays</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue X</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Autumn 2025</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-note">Tenth-issue anthology</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue IX</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Summer 2025</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-note">Coastal fiction</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue VIII</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Spring 2025</span>
</li>
<li class="rp-archive-row">
<span class="rp-archive-num">Issue VII</span>
<span class="rp-archive-sep">·</span>
<span class="rp-archive-season">Winter 2024</span>
</li>
</ol>
</section>
/about.php
Same outcome for the /about.php section, there's just a description with redirects to the dashboard, /archive.php and /submit.php.

<section class="rp-longform">
<p class="rp-dropcap">Ridgeline Press began as a stapled chapbook passed around a reading group in 2019. Six years and thirteen issues later, it is still small, still stapled (mostly), and still run by three people who read every submission ourselves.</p>
<p>We publish fiction, personal essays, and poetry from the Pacific Northwest and anywhere else the weather turns honest. Our bias, if we have one, is for work that earns its quiet. We like pieces that notice things. We like sentences that have had time to settle.</p>
<p>We are not a magazine of the moment. We publish four issues a year and read on a slow calendar. Our rejections arrive handwritten more often than not. When we accept something, we pay a modest honorarium and send two printed copies to the author's mailbox.</p>
<h2 class="rp-longform-subhead">The editors</h2>
<p><strong>Hannah Alder</strong> — editor-in-chief, reading for fiction. Hannah founded the press in 2019 and edits most of what runs here. She lives in a rented barn outside a small ferry town.</p>
<p><strong>Silas Vance</strong> — poetry editor. Silas curates the poetry folio and reads most of the unsolicited verse submissions personally. He teaches at a regional college and writes his own poems very slowly.</p>
<p><strong>Mike Renner</strong> — production & essays. Mike handles the print layout, ships the subscription boxes, and keeps this website online with the help of one PHP file that he swears he'll rewrite someday. He reads and edits for nonfiction.</p>
<p class="rp-longform-coda">Interested in what we've published? Start with the <a href="/">current issue</a> or take a stroll through the <a href="/archive.php">archive</a>. Thinking of submitting? Please <a href="/submit.php">read the guidelines</a> first — they are short, and they matter to us.</p>
</section>
/submit.php
This section also doesn't offer anything of value to us, the online submission form is unavailable, down for construction and only e-mail submissions are allowed according to the app.

Back to the dashboard we go to see the current issue!
/piece.php
We are going to back to that frontend code from the very get-go.
<section class="rp-section">
<div class="rp-section-heading">
<h2 class="rp-section-title">In this issue</h2>
<span class="rp-section-count">6 pieces</span>
</div>
<div class="rp-pieces">
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-fiction">Fiction</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=tidewater-by-anna-mills.html">Tidewater</a>
</h3>
<div class="rp-piece-byline">by Anna Mills</div>
<p class="rp-piece-excerpt">The ferry was late again, and the tide was coming in fast enough to matter.</p>
<a class="rp-piece-read" href="/piece.php?slug=tidewater-by-anna-mills.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-essay">Essay</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=the-green-door-by-james-burrows.html">The Green Door</a>
</h3>
<div class="rp-piece-byline">by James Burrows</div>
<p class="rp-piece-excerpt">My grandfather kept a door in the shed that led nowhere. This is the story of why.</p>
<a class="rp-piece-read" href="/piece.php?slug=the-green-door-by-james-burrows.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-poetry">Poetry</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=low-tide-in-october-by-claire-nishimoto.html">Low Tide in October</a>
</h3>
<div class="rp-piece-byline">by Claire Nishimoto</div>
<p class="rp-piece-excerpt">Three poems on the rhythm of the coast and the small things it leaves behind.</p>
<a class="rp-piece-read" href="/piece.php?slug=low-tide-in-october-by-claire-nishimoto.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-essay">Essay</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=field-guide-to-saltgrass-by-david-okonkwo.html">A Field Guide to Saltgrass</a>
</h3>
<div class="rp-piece-byline">by David Okonkwo</div>
<p class="rp-piece-excerpt">On a plant nobody looks at, and what it quietly holds together.</p>
<a class="rp-piece-read" href="/piece.php?slug=field-guide-to-saltgrass-by-david-okonkwo.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-fiction">Fiction</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=two-letters-by-marta-escribano.html">Two Letters</a>
</h3>
<div class="rp-piece-byline">by Marta Escribano</div>
<p class="rp-piece-excerpt">One was meant to be sent. The other was meant to be kept.</p>
<a class="rp-piece-read" href="/piece.php?slug=two-letters-by-marta-escribano.html">Read the piece →</a>
</article>
<article class="rp-piece-card">
<div class="rp-piece-meta">
<span class="rp-piece-category rp-cat-essay">Essay</span>
</div>
<h3 class="rp-piece-title">
<a href="/piece.php?slug=what-the-ferry-taught-me-by-ben-kimura.html">What the Ferry Taught Me</a>
</h3>
<div class="rp-piece-byline">by Ben Kimura</div>
<p class="rp-piece-excerpt">Forty-one crossings, one schedule, and a quiet argument with my own hurry.</p>
<a class="rp-piece-read" href="/piece.php?slug=what-the-ferry-taught-me-by-ben-kimura.html">Read the piece →</a>
</article>
</div>
</section>
We can see that there's a slug parameter that is being used to read .html files.
Let's open one of the pieces to see if there's anything of note within or if we should focus on that slug parameter.

Nothing of note, so we know where we should focus. As a side-note, we can open /robots.txt, this would be part of our normal enumeration technique when we would need to be finding hidden directories or places where we shouldn't have access.

Unfortunately, when we try to open /notes.html regularly through the browser, we get a 403 Forbidden response.

Exploitation
notes.html is just another .html file just like the files that the slug parameter is taking.

What if we try to give notes.html to slug?

Okay, so this tells us a lot of information, we can give slug files that are located in pieces/ and it would give them to us, unfortunately we don't know where notes.html is located, and we know we are currently located in pieces/ , what if we try to go up one directory?
https://portswigger.net/web-security/file-path-traversal
../ moves one directory up in the filesystem, allowing access outside the intended folder.

Well well well, if it isn't the answer to all our questions, now we know where the flag.txt is located and we know that it should be in the home directory of Mike! But not only that, this successful call proves that we have file path traversal on the machine letting attackers read all the information on the machine, such as /etc/passwd. User input is directly concatenated into a filesystem path without validation which means: Path Traversal (LFI).
The only issue is that we don't know the current location of pieces so we need to guess how many directories we need to traverse.

Eitherway, let's not get ahead of ourselves and find the flag.

Post-exploitation
We can try to find the vulnerable code that lets us analyze the vulnerability more in detail and how it could be fixed.
For some reason, I can't find /piece.php, but we can find /index.php for now.

The thing is, when we tried to get /piece.php in the current working directory for example, this is the output:

and when we try to go one directory up we get the following result:

This seems weird considering that when we tried to get /etc/passwd, before finding the depth we were getting errors such as:

The difference is that if the file doesn't exist, at least the name gets printed out. Which means that when we try to get ../piece.php if we inspect the page source code, we can see:

The issue is that the PHP code gets rendered, so we can't really see it on the frontend, and we see that weird error.
<article class="rp-piece">
<?php
// Individual piece viewer.
//
// Pieces are stored as HTML fragments in pieces/. The slug in the URL
// includes the extension — `?slug=tidewater-by-anna-mills.html` — so we
// don't have to worry about appending one. Simpler for a three-person
// editorial team, easier for Mike to maintain.
$slug = $_GET['slug'] ?? 'welcome.html';
$path = __DIR__ . '/pieces/' . $slug;
$page_title = 'Reading Room';
require_once __DIR__ . '/header.php';
?>
<article class="rp-piece">
<?php
if (file_exists($path)) {
readfile($path);
} else {
?>
<div class="rp-notfound">
<h2 class="rp-notfound-title">Piece not found</h2>
<p class="rp-notfound-lede">
No piece at <code>pieces/<?= htmlspecialchars($slug) ?></code>.
</p>
<p class="rp-notfound-sub">
Check the <a href="/">current issue</a> or browse the <a href="/archive.php">archive</a>.
</p>
</div>
<?php
}
?>
</article>
Now let's break it down:
$slug = $_GET['slug'] ?? 'welcome.html';
$path = __DIR__ . '/pieces/' . $slug;
This piece of code:
- Takes user input directly from URL (
slug) - Appends it to a filesystem path
- No validation at all
Meaning that ?slug=tidewater.html becomes: /var/www/html/pieces/tidewater.html .
And now the problematic part:
$path = __DIR__ . '/pieces/' . $slug;
The base directory is hard-coded and since we take user supplied input for $slug, the user can just use traversal tricks to escape the hard-coded directory.
if (file_exists($path)) {
readfile($path);
}
If the file is found then readfile() is called on the unsanitized path, which means we can read any system file.
Hope that made sense, the Foundations lab pool is is growing with these different vulnerabilities :D.