Summary
You have been contracted by Sumace Consulting Gmbh to carry out a web application penetration test against their main website. During the kickoff meeting, the CISO mentioned that last year's penetration test resulted in zero findings, however they have added a job application form since then, and so it may be a point of interest.
Objective:-Assess the web application and use a variety of techniques to gain remote code execution and find a flag in the / root directory of the file system.
(This lab directs us to look only in the application page for vulnerabilties, but we will do our due diligence 😉)
THE WEBSITE



Hunting for Local File Inclusion (LFI)
First things first, we start by thoroughly mapping the website, making sure no stone is left unturned. Our primary goal during this enumeration phase is to identify any parameters that are fetching resources from the backend — like files, images, or scripts. These are prime targets for Local File Inclusion (LFI) vulnerabilities.
After a deep dive into the application's functionality, two interesting parameters caught my eye.
The first one surfaced after submitting a dummy job application. Upon submission, the application redirects to a thanks.php page, which takes the name we inputted in the form and reflects it in the URL via the n parameter (e.g., thanks.php?n=Rishi).
My immediate thought was to test this for LFI. I threw a few manual directory traversal payloads at it, but didn't get any bites. To be completely thorough, I decided to fuzz the parameter using ffuf and the classic Jhaddix LFI wordlist provided in the HTB module:
Bash
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://154.57.164.81:30662/thanks.php?n=FUZZ'Unfortunately, this turned out to be a dead end. We didn't get any significant results, so it was time to pivot.

Moving on, I spotted our second potential vector while inspecting how the site loads images. I noticed the application uses an API endpoint, fetching images via a p parameter (/api/image.php?p=...) [also found a application.php page] . The actual filename being passed was a bit unusual (we'll dive into why that matters later), but the crucial takeaway was that we had a highly suspicious parameter to test.
I fired up ffuf again to fuzz this new endpoint:
Bash
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://154.57.164.81:30662/api/image.php?p=FUZZ'This time, the results looked promising! However, the terminal output was incredibly noisy. I noticed that a massive chunk of the payloads were returning a response size of 0. This meant the server was returning an empty page for those failed attempts.
To clean up the terminal output and focus only on the successful hits, we can filter out those empty responses using the -fs 0 (filter size 0) flag. This tells ffuf to hide any response with a size of zero bytes:
Bash
ffuf -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://154.57.164.81:30662/api/image.php?p=FUZZ' -fs 0
Now that ffuf had successfully identified a working payload, it was time to manually verify the vulnerability and inspect the output.
I switched over to Burp Suite, sifted through my HTTP Proxy history, and located the GET request for /api/image.php. I sent this request over to Burp Repeater (Ctrl + R / Cmd + R) so I could easily manipulate the p parameter and analyze the server's HTTP responses in real-time.
I replaced the legitimate image filename with the payload that triggered our successful fuzzing hit: ....//....//....//....//etc/passwd. I hit "Send," and immediately got a 200 OK response. GOT our LFI.

If you look closely at the payload, you'll notice we aren't using the standard ../ for directory traversal. Instead, we are using ....//. This indicates that the application is actively trying to sanitize our input by stripping out any instances of ../ to prevent traversal attacks.
However, this is a flawed defense mechanism. By sending ....//, the application's filter strips out the inner ../, which causes the remaining outer characters to squash together. This forms a brand new, unfiltered ../, successfully bypassing the Web Application Firewall (WAF) or backend sanitization and allowing us to traverse up the directory tree!
We needed to use this LFI to read the underlying PHP source code of the web application. By traversing back into the web root, we can pull the source code for the pages we discovered during our initial enumeration (like the thanks.php file or the image.php API itself).




Now there are quite interesting things in these source codes we will talk about it one by one.
Analysis of source codes
Using our newly weaponized LFI, I started pulling the source code for the application's internal files to understand the backend logic. The dummy job application page we tested earlier felt like a logical starting point.
By reading the source code of the job application page, I discovered that the form submissions were actually being processed by a separate backend script located at /api/application.php. Naturally, I used our LFI payload to read the source code of application.php next.
This is where we struck gold. The PHP source code revealed the exact naming convention and routing logic the server uses when a file is uploaded.

When a user uploads a file (like a resume), the backend script does the following:
- It takes the uploaded file and calculates its MD5 hash.
- It renames the file to that MD5 hash, while preserving the original file extension (e.g.,
.php,.pdf,.jpg). - It saves the renamed file into the
../uploads/directory.
Upon further inspection of the code, I noticed a massive security flaw: there are absolutely no file type validations or security checks in place. The application blindly trusts whatever we upload. This is a textbook Unrestricted File Upload vulnerability.
RCE Hunting
At this point, the entire attack path became clear. We have all the necessary ingredients to escalate this into Remote Code Execution (RCE)
At this point, the attack path was coming together, but we hit a slight roadblock. Upon investigating the source code of image.php, I noticed it only fetches the contents of the requested file and displays it using an echo statement (acting essentially as a read-only function). This meant that while we could read files, executing our uploaded PHP shell was not possible here. We needed to find another hidden parameter (😉).

Upon looking more closely than i already did (🥸), I found exactly what we needed in contact.php.
This file processes a completely different parameter and, crucially, uses the include function. As we established earlier, include grants us the execute privileges required to trigger our shell, rather than just the read privileges provided by image.php.


So the script uses 1. a region parameter 2.It checks for common directory traversal characters (specifically . and /). If it detects them, it flags the input as dangerous and throws an invalid character warning. 3. if bypassed give us execute privileges using the include function.
The Final Attack: Bypassing the Filter for RCE
At this point, we have collected all the pieces of the puzzle. We have:
- An unrestricted file upload vulnerability.
- The exact naming convention of our uploaded files (MD5 hash).
- The location where they are saved (
../uploads/). - An LFI vulnerability in
contact.phpthat we can escalate to RCE.
To kick things off, I created a simple PHP web shell named shell.php with the following content:
PHP
<?php system($_GET['cmd']); ?>(Note: Ensure no spaces after the command end)
Now I md5 the file and copied the hash.

Now, it was time to craft our LFI payload in the contact.php page using the region parameter, appending &cmd=ls at the end to test for Remote Code Execution.
(Note: Remember from our source code review that the script automatically appends the .php extension to the region parameter, so we don't need to include the extension in our payload!)
I sent the first request using standard directory traversal characters (../uploads/[HASH]&cmd=ls). As expected from the str_contains filter we saw earlier, the payload was instantly blocked, and the application threw an error message.
Next, I tried bypassing the filter using standard URL encoding (%2e%2e%2f). Surprisingly, this was also blocked! The backend was likely decoding the request once before hitting the filter.
took it a step further and tried double URL encoding the traversal sequence (%252e%252e%252f). I fired off the request:

The filter was completely bypassed. Looking at the page response, the output of the ls command was printed in the page. We successfully achieved Remote Code Execution.
With a working shell, the final objective was right in front of us. I needed to locate the flag, so I updated my command to list the contents of the root directory by changing the parameter to &cmd=ls /.

Scanning the output, I quickly spotted our target: the flag text file.
To claim the victory, I updated the command one last time to &cmd=cat /flag.txt (substituting the exact filename found). The server executed the command, and the flag was printed right there on the screen.

Box completely compromised, FLAG found.
