RocketChat - Monitor User Messages

Background

During a recent engagement, a client was using an updated version of Rocket.Chat with registration disabled. Since services like Rocket.Chat, Slack etc may contain internal and sensitive information, I wanted to see if I could find a way into the service or get a unauthenticated read access to some of the channels and messages. In the end, I identified a way to read last messages of any rooms without any authentication. This could then be leveraged with a script to monitor messages sent to the channel.

Vulnerability

What are rooms (rid)

In Rocket.Chat, rooms are ways you can communicate with other users. Some examples are: channels, direct messages, and group messages. rids are unique IDs that help identify these rooms. While they are unique, some of them are easy to guess:

  • #general channel’s Room ID is GENERAL

  • rids for direct messages are concatenation of the user’s IDs. For example, a rid for private message could be UserA’s ID + UserB’s ID.

When messages are returned, the ID of the user who sent it is also returned. This makes it easier to identify all potential direct messages between users.

Vulnerable Function

Similar to my last vulnerability, I wanted to identify vulnerable methods (functions that could be called via Meteor.call) that had no authentication checks. In Rocket.Chat most of the authenticated checks in methods are done by a simple check.

const user = Meteor.user() as IUser | undefined;
if (!user) {
	throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'sendFileMessage' } as any);
}

In the snippet above, the code checks if user ID is undefined. An undefined or null user ID indicates that the request is unauthenticated and it returns error-invalid-user.

While going through these methods, canAccessRoom method stood out.

Screen Shot 2021-10-13 at 10.35.39 AM.png

User ID validation

This specific method took two parameters: rid and userID. The userID passed into the function is provided by the user rather than through the Meteor.user() session controller. Once called, the function checked if the userID was null or undefined. After the validation, it would then pull the user information.

Screen Shot 2021-10-13 at 10.40.36 AM.png

Room and Access validation

Next, the room validation is done by checking:

  • If given rid is null/undefined

  • If the rid is not null, get the room information via findOneByID

  • canAccessRoom authorization check is called

The functionality did not check to see if the current user had the right to query the message of another user (admin, or the user themselves). As a result, it was possible to call canAccessRoom with a userID and a rid to view the last message. There are couple ways you can retrieve the User IDs without authentication so this was not a blocker either.

After finding this vulnerability, I signed up on open.rocket.chat with two different user accounts and created dummy/flag messages. After that, I called the function with authentication and confirmed this worked on the latest version deployed to Rocket.Chat community as well.

Screen Shot 2021-10-13 at 10.54.24 AM.png

Report Timeline

  • September 24, 2021 - Reported to Rocket.Chat security

  • September 30, 2021 - Vulnerability confirmed by Rocket.Chat security

  • November 05, 2021 - Vulnerability fixed as part of 4.1.1 update

  • November 25, 2021 - Vulnerability disclosed here

Next
Next

RocketChat - Unauthenticated access to messages