A couple of years ago I looked at Razer Comms. I found a bunch of stuff that I never reported or pursued. I discovered the application is now retired so I am publishing these.
I did not look very hard but Razer Comms was essentially a webapp running via the Chromium Embedded Framework. There were no checks on channel authorizations. You could read every channel including ones protected with passwords.
You can see my notes at https://github.com/parsiya/Parsia-Clone/tree/master/research/razer-comms.
These are some of the stuff that I found. I wish I had looked into some of the stuff more. Especially the parts about what could be done when I got XSS.
At work I provide similar information in my report because I think Steps to Reproduce
are an important part of any report. The client, dev team and the next person who comes for the next iteration of the app need to be able reproduce what I did.
The Razer Comms application does not perform any kind of input validation or sanitization on user input in community chat channels. This leads to stored cross-site scripting in community chats in the application, in-game overlay and in the web interface.
Community chat channels utilize both HTTP based requests and an XMPP server (located at comms-cxmpp.razersynapse.com:5222
) using WebSockets to post and retrieve messages. Based on observation it seems that XMPP is used to provide the real-time chat while the messages sent through HTTP are actually stored on the server. For example if different messages are sent with the same message ID to the server via the XMPP protocol and a PUT request, the XMPP message is displayed instantly while the message sent via the PUT request is stored on the server and later displayed when the chat room is reloaded or if any user joins the channel after the message is posted.
The message is wrapped in <span>
tags and sent to the server (in both XMPP and HTTP) and is reflected back as-is. The application uses Chromium Embedded Framework or CEF to display community chats which are essentially HTML pages with CSS to display the messages. Each community chat room is also available through a web portal using the same HTML structure.
The JavaScript is not able to escape the CEF (although it is run with the --no-sandbox
command line parameter) and modify anything in the application. However, it is possible to inject malicious JavaScript to trick users into entering their password or make the chat channels unusable. It is also possible to inject HTML to display spoofed messages (e.g. messages displayed from a different user such as an administrator to direct users to use a malicious website).
The chat web interface has the same issue. Each community's chatroom is also available via web at the https://comms.razerzone.com/communities/chatpanel/index.html?id={{communityID}}
URL. XSS payloads are executed in users' browsers if they navigate to the web-based chatrooms.
User's can access chat channels from the in-game overlay. The in-game overlay does not execute all JavaScript. For example alert boxes are not displayed but they prevent users from viewing or posting anything in chat via the in-game overlay making them unusable.
Steps to Reproduce (simple JavaScript prompt injected from the application):
Proxy (menu) > Options (sub-menu) > Intercept is On (button)
.PUT
request to the following URL with the message in its body:<script>prompt('Please enter your password', 'Password')</script>
and disable Burp interception by clicking the Intercept is On
button.
Intercepted PUT request modified in Burptest
) sent via XMPP.
Chat channel before reloadingInspect Element
(or a similar item based on the browser) to view the injected JavaScript.
Web interface XSS sourceSteps to Reproduce (simple JavaScript prompt injected from the web interface):
http://comms.razerzone.com/communities/preview/?id={{communityID}}
.Proxy (menu) > Options (sub-menu) > Intercept is On (button)
.PUT
request to https://api.comms.razerzone.com/communities/{{communityID}}/channels/{{chatroomID}}/messages/{{messageID}}
with the message in its body.<script>prompt('Please enter your password', 'Password')</script>
and disable Burp interception by clicking the Intercept is On
button.test
) sent via XMPP.Steps to Reproduce (Wreck chat channels)
123</span></p></div>HELLO
Community chat unusable
The Location/City
field in user profile is vulnerable to stored cross-site scripting (XSS). User profile is essentially a HTML page which is displayed in a browser using Chromium Embedded Framework (CEF) similar to chat rooms. Any injected JavaScript in the Location/City
field will be executed in Razer Comms when that profile is viewed.
The following fields do not accept arbitrary input. For example age
only accepts numbers so I could not inject anything malicious:
Server performs input validation on the following fields. For example everything between angle brackets is removed:
Server performs output encoding on the following field. Angle brackets are encoded to <
and >
:
A sample request for modifying the user profile:
POST /1/user/post HTTP/1.1
Content-Type: application/xml
Host: ecbeta.razerzone.com
Content-Length: 940
Connection: close
<COP>
<User>
<ID>RZR_ID</ID>
<Token>[token removed]</Token>
<LastName access="private">lastname</LastName>
<FirstName access="private">firstname</FirstName>
<Nickname>user2</Nickname>
<BirthYear access="private">1980</BirthYear>
<BirthMonth access="private">1</BirthMonth>
<BirthDay access="private">1</BirthDay>
<Gender access="private">Female</Gender>
<City access="private">location</City>
<Country access="private">AL</Country>
<UserLanguage access="private">en</UserLanguage>
<AboutMe>status</AboutMe>
</User>
<ServiceCode>0020</ServiceCode>
</COP>
Note: Because profile data are sent in an XML payload through the body of the PUT
request, injection payloads containing special characters need to be smuggled in a CDATA
tag:
<![CDATA[<script>alert(1)</script>]]>
Part of the profile:
<body oncontextmenu="return false;">
<div id="body">
<div id="section-header" class="about-slider">ABOUT</div>
<div id="inner-data" class="about-sliding-div">
<table id="avatar-about-me" border="0" cellspacing="0" cellpadding="0">
<tr>
<td class='fade' id='status-icon'>
<img width='6' height='70px' src='images/status/online.png'/>
</td>
<td class="fade" id="avatar"><img width="70" height="70" src="images/avatar.png"/></td>
<td id="name-about-me">
<div class="fade1 ellipsis_div" id="name">firstname lastname</div>
<div class="fade1 ellipsis_div about-wrap" id="about-me">"status1<script>alert(1)</script>"</div>
</td>
</tr>
</table>
<div style="margin-top:12px"></div>
<div id="info-div">
<table id="info-table">
<tr id="info-row">
<td id="info-header" scope="row">Nickname</td>
<td class="fade2" id="info-data"><div class="ellipsis_div">nickname</div></td>
<td></td>
</tr>
<tr id="info-row">
<td id="info-header">Comms ID</td>
<td class="fade3" id="info-data-razer-id"><div class="ellipsis_div">comms ID</div></td>
</tr>
<tr id="info-row">
<td id="info-header">Date of Birth</td>
<td class="fade4" id="info-data"></td>
</tr>
<tr id="info-row">
<td id="info-header">Age</td>
<td class="fade5" id="info-data"></td>
</tr>
<tr id="info-row">
<td id="info-header">Gender</td>
<td class="fade6" id="info-data"></td>
</tr>
<tr id="info-row">
<td id="info-header">Language</td>
<td class="fade7" id="info-data"><div class="ellipsis_div">English</div></td>
</tr>
<tr id="info-row">
<td id="info-header">Location</td>
<td class="fade8" id="info-data"><div class="ellipsis_div"></div></td>
</tr>
</table>
</div>
</div>
Steps to Reproduce:
EDIT PROFILE
.Proxy (menu) > Options (sub-menu) > Intercept is On (button)
.Friends
and close the window. Remember to fill in at least Location
and Country
, otherwise the access level for those fields will not be modified.PUT
request to https://ecbeta.razerzone.com/1/user/post
.City
, FirstName
, LastName
and Nickname
fields to <![CDATA[<script>alert(1)</script>]]>
and turn off Burp's interception functionality (this will forward the modified request).View Profile
.XSS in profile
The XMPP server does not check if the person sending the message is the same as the one in the message. Messages can be spoofed to have come from anyone. But XMPP is only used to present chatrooms' realtime functionality. Messages stored on the server are the ones sent by the PUT
requests and are stored server-side. As a result it's only possible to spoof messages in realtime and not stored.
Steps to Reproduce:
Proxy (menu) > Options (sub-menu) > Intercept is On (button)
.sender_name
element.
Sender name modified in intercepted requestPUT
request) has the correct sender name.
Stored message after refresh displays the correct sender nameThe Razer Comms server seems to lack authorization controls for many actions. For example any user can access the contents of any password protected channel or post messages to it. Or create a sub-channel without permissions. This is dangerous as users can do all of this by knowing communityIDs and channelIDs which are not secret and can be retrieved by any user.
Fortunately it is not possible to delete communities as a normal member. The communities are deleted using the DELETE
HTTP method and normal members receive a 403 Forbidden
response when attempting to delete communities without having access.
Communities cannot be deleted
The server does not perform any authorization checks for read/post requests to password protected chat channels. Any user can read the contents of them and/or post messages. The only information needed for these actions are communityIDs and channelIDs which are public information. Any user can query a specific community and retrieve channel IDs.
For this test, two users were created. One user created a private community. Within the community a password protected channel is created. Another user is invited to the community and joins. The second user should not be able to view the contents of the password protected channel and post to it without knowing the channel password.
Steps to Reproduce (posting messages and reading the contents of a password protected channel):
Response
tab to view the response to this request. The response contain the lobby ID.
Lobby IDSend to Repeater
from the context menu.
Request that retrieves all sub-channels"protected": true
).
ID and properties of the password protected channelProxy (menu) > Options (sub-menu) > Intercept is On (button)
.PUT
request to https://api.comms.razerzone.com/communities/{{communitID}}/channels/{{channelID}}/messages/{{messageID}}.
Intercepted chat message requestResponses leak internal IPs.
Internal IPs in responses