By kernelpanic, VAPT/Pentesting Specialist (Former IT Infrastructure Engineer)
️ Legal & Ethical Disclaimer: The information provided in this article is for educational purposes only. The techniques demonstrated here are performed in a controlled lab environment. Do not attempt to use these methods on any system or network without explicit, written authorization. Use this knowledge to improve your skills in Penetration Testing and Bug Hunting the right way. Stay ethical.
Valentines might not be in the cards for me in real life, but who needs a date when you have a terminal? While the rest of the world is busy with roses and dinner reservations, I’m busy finding vulnerabilities and committing to the “one” thing that never lets me down: a successful exploit chain.
We’ve all been there. It’s a quiet weekday. Your main VAPT engagement is in a lull, the tickets are cleared, and you’re just… bored. For a security professional, “boredom” often means looking for something to break. Instead of focusing on romance, I decided to focus on TryHackMe.
I spun up the ValenFind lab. What looked like a simple dating site feature quickly became a masterclass in Attack Chaining — the exact mindset needed for modern Red Teaming. If you are a pentester looking to demonstrate the high impact of “low” vulnerabilities, this one’s for you.
Let’s break the chain, step-by-step.
Part 1: Initial Reconnaissance & The Suspect Input
Our hunt begins with analyzing how the application handles user input. Any parameter is a potential vector. I always run my traffic through Burp Suite Professional, and a specific request immediately caught my attention:
Press enter or click to view image in full size
The URL parameter ?layout=theme_modern.html is a red flag. It suggests the application is dynamically loading a file from the backend filesystem based on this input.
Get k3rnelpan1c’s stories in your inbox
Join Medium for free to get updates from this writer.
A seasoned pentester asks: Can I control this path to read other files?
Part 2: Confirming the Primitive (Local File Inclusion)
The first step in chaining is confirming a vulnerability primitive. We manipulated the input by attempting directory traversal. A simple payload, ../../../../etc/passwd, was used to see if we could break out of the intended web root.
Press enter or click to view image in full size
The server’s response confirms a critical vulnerability: Local File Inclusion (LFI). The server does not sanitize the user’s input. This is our foothold.
Part 3: The Attack Architecture (Visualization)
To understand how this input manipulation translates into deeper system access, let’s visualize the attack flow we’ve just executed.
Press enter or click to view image in full size
This diagram is key. It shows that our ultimate target isn’t just user files, but the Application Logic itself (app.py).
Part 4: The Heist (Information Disclosure)
With LFI confirmed, we targeted the source code. By reading app.py, we executed a Source Disclosure attack, turning our "black box" test into a "white box" audit.
Press enter or click to view image in full size
The results were devastating:
- Hardcoded Credentials:
ADMIN_API_KEY = "CUPID_MASTER_KEY_2022_XYXY". - A Hidden Route: We discovered
/api/admin/export_db, designed to export the entire user database.
Part 5: The Payoff (Data Exfiltration)
A developer’s mistake (hardcoding a secret) provided the authentication bypass we needed.
Press enter or click to view image in full size
Using curl, we impersonated the administrator by passing the stolen token in the X-Valentine-Token header. The server trusted our "identity" and exfiltrated the entire SQLite database to our machine.
Part 6: Looting the Database & Final Findings
We had the loot. The final step was to access the sensitive data and secure the flag.
Press enter or click to view image in full size
Opening the database with sqlite3 was the end of the chain. We dumped the table and found the system flag. The flag contained a humorous nod to the developer's "vibe coding": THM{my_c0ding_1s_n0t_y0ur_cup_0f_cha!}.
Points to Take Away
- Input is Never Safe: Even decorative parameters like
layoutcan lead to full system compromise if not sanitized. - Chain for Impact: An LFI by itself might be “Medium” severity, but chained with Source Disclosure and Auth Bypass, it becomes “Critical.”
- Never Hardcode Secrets: Storing API keys in code is a death sentence for application security.
- Boredom is a Tool: Use your downtime to practice methodology; one day, these skills will be the foundation of your professional future.
Part 7: The Way Out (Remediation & Analysis)
As security professionals, our value comes from showing how to fix it.
Remediation 1: Stop the LFI
Use an allowlist of valid file names instead of trusting user input.
# Secure Code (Sanitized with Allowlist)
@app.route('/api/fetch_layout')
def fetch_layout():
layout_file = request.args.get('layout')
ALLOWED_LAYOUTS = ['theme_modern.html', 'theme_retro.html'] if layout_file in ALLOWED_LAYOUTS:
with open(f"templates/layouts/{layout_file}", "r") as f:
return f.read()
return "Error: Layout not found.", 404
Remediation 2: Secure Secrets Management
Use environment variables rather than hardcoding.
# Secure Code (Using Environment Variables)
import os
ADMIN_API_KEY = os.environ.get('CUPID_ADMIN_API_KEY')Conclusion
That’s how you handle a bored weekday when there’s no Valentine in sight. What started as looking for a file turned into a complete database heist.
For your VAPT engagements and certifications like eJPT, always remember: stay sharp, stay curious, and always look for the chain.