The Archives
Challenge Description
Warehouse incident reports are kept in a separate directory. The file server was built quickly. Corners were cut.
Flag: Raptor{flag4_p4th_tr4v3rs4l_d1r3ct0ry_3sc4p3}
Following Prior Recon
The mention of incident reports was an immediate callback to Web 1: /api/internal/incidents was sitting right there in robots.txt. Visiting it:
{
"classified": true,
"message": "Access to incident logs requires Level 5 clearance. Contact your WHAM! supervisor.",
"recent_count": 47,
"suppressed_count": 44
}
Locked behind clearance. The flavor text said "separate directory" and "file server built quickly", that's a nudge toward path traversal. Tried the obvious:
/api/internal/incidents/../../../../flag.txt
404. The incidents endpoint wasn't the attack surface, it was just the hint pointing at the theme. Time to go broader.
API Enumeration with ffuf
Ran ffuf against the base /api/ path with the SecLists API endpoints list:
ffuf -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints-deep.txt \
-u https://whamazon.strayerraptors.com/api/FUZZ
Got hits back, but two things stood out immediately:
1. /api/jobs was returning unusually low word count compared to every other result, likely minimal or empty responses worth investigating. And/api/cart was returning a 401, likely just because no Cookie was provided.
2. We only got 275 results when the wordlist has 276 lines. One endpoint wasn't returning anything. No 200, 404, nothing registering in the output.
So I pulled the results into LibreOffice Calc and did a diff against the full wordlist. The missing entry: /api/images.
No response at all from ffuf which meant it was probably being filtered or timing out under specific conditions, not that it didn't exist. Visiting it directly confirmed it:
{"message": "Missing 'file' parameter"}
The endpoint is alive and expecting a file parameter. A file server that takes a filename as a parameter and was built quickly with corners cut, that's got path traversal written all over it.
Exploitation
/api/images/?file=../../../flag.txt
Raptor{flag4_p4th_tr4v3rs4l_d1r3ct0ry_3sc4p3}
The file parameter was being passed directly to a file read operation with no sanitization, allowing traversal out of the intended directory using ../ sequences.
The Missing Result Anomaly
It is worth calling this out specifically because it could have been easy to miss. When fuzzing, a missing result is just as interesting as an unexpected one. In this case /api/images was silently absent from ffuf output, possibly due to a timeout, an unusual response format, or a response code that didn't match the filter. Always cross-reference your results against the source wordlist, especially when the math doesn't math.
Key Takeaways
Path traversal happens when user-controlled input is used to construct a filesystem path without validation. The fix is straightforward: resolve the canonical path of the requested file and verify it falls within the intended base directory before serving it. Something like:
import os
BASE_DIR = "/var/www/images"
requested = os.path.realpath(os.path.join(BASE_DIR, user_input))
if not requested.startswith(BASE_DIR):
abort(403)
The broader lesson here is that good recon compounds. The robots.txt from Web 1 didn't solve this challenge directly, but it seeded the right mental model: incident reports, internal directories, something file-related. That pointed toward the right attack surface once the API enumeration surfaced /api/images.