Ad Hide

Finding websocket data leak in

As an avid gamer I will always support local esport services. One of them being Kayzr (previously besports), a Belgian based esports tournament organiser. While using their website I noticed a LOT of javascript, my analyzer gave Ink, Meteor, Moment.js, React, underscore.js, etc... And with a lot of client-sided generated content comes a lot of risks.

Websocket is well build with a ton of websocket traffic in order to load all the different modules on the site which creates a fast - but not very responsive ;) - website with some very cool features. With chrome we can analyze all this traffic.

Chrome Websocket Traffic

Whilst inspecting all the traffic while browsing the site my eyes fell on this reply. More specifically the 'password' field is what stood out the most.
a["{"msg":"added","collection":"users","id":"***CUT***","fields":{"createdAt":{"$date":1456509130107},"services":{"password":{"bcrypt":"***CUT***"},***All personal information and IP's***
Out of nowhere I saw my personal information flash by: home address, email, my logged in IP's, what I assume my hashed password (probably salted, cross checked the regular hash of mine) and hashed session tokens. This is a way to much information to be send to back to a user. In the mess of the websocket traffic I didn't immediately find the correct request which triggered this response, so I kept browsing until I saw it again. Surely enough it returned a lot and found the evil function. Side note: 'usernameFromId' implies the function should only return the id from the user and not a full data dump of a user.
["{"msg":"sub","id":"***Random ID***","name":"usernameFromId","params":["***My user ID***"]}"]
In order to recreate the request I made my own websocket to connect to theirs with fancywebsocket.js. To succesfully create a connection I recreated their 'handshake'.
// log() starts on websocket response and logs it to a textarea
function log( text ) {
	$log = $('#log');
	if(text == 'o'){
	log( "sending preload" )
	// At first I included the authentication requests but after 
	// further inspection the requests worked unauthenticated
	if(text.indexOf("ping") !== -1){
	// invokes pong reply from server
	// send('["{\\"msg\\":\\"ping\\"}"]');
When the connection was successfully made, I send the 'evil' method and it returned with all my personal info, awesome! Upon repeating the same request the server responded with an error and closed the connection. Strange. After some inspection of the id's I noticed the first 'id' is random generated used for tracking requests and when using the same id multiple times in one session the server closes the session. Now I needed the id's from other users. When using the search function on the site I found the exact function I needed.
And as expected, the server returned the id's of the found usernames. And after an unauthenticated request with my own websocket, targeting my own account and bingo!, it replied.

Reply of the server

And at the end of the reply.

End of the reply of the server

15th of October, 2016 ~2:30am: Found leak, I was tired, went to sleep
15th of October, 2016 ~3:00pm: Confirmed the leak
15th of October, 2016 3:27pm: First contact with Kayzr
15th of October, 2016 3:41pm: Kayzr replied and asked to confirm with given username
15th of October, 2016 3:44pm: I replied
15th of October, 2016 3:55pm: Kayzr confirmed the error and deployed ASAP
15th of October, 2016 4:23pm: Kayzr asked to try again, leak was fixed
15th of October, 2016 4:33pm: Rewarded: 69.000 coins = A crate (24) Jupiler pils

This wasn't a very difficult vulnerability but still one with a huge impact. Overall I'm very happy with the response of Kayzr, even though they don't have bug bounty program they greatly appreciated my help. This leak could have been used to get IP addresses of contestants of their hosted tournaments and DDOS, which can be very impactful in a game like CS:GO. Or to get home addresses for sore losers.


Get notified!

Join the list and get notified when new blog posts are added!