Loading HuntDB...

Pinning leaks message content

High
R
Rocket.Chat
Submitted None
Reported by gronke

Vulnerability Details

Technical details and impact analysis

Information Disclosure
**Summary:** Improper input validation allows pinning of arbitrary messages (in private channels), leaking the message content back to the sender. **Description:** Message pinning was found to lack input data validation, so that arbitrary messages can be pinned and leaked back to an unauthorized client. ```javascript Meteor.methods({ pinMessage(message) { if (!Meteor.userId()) { toastr.error(TAPi18n.__('error-not-authorized')); return false; } if (!settings.get('Message_AllowPinning')) { toastr.error(TAPi18n.__('pinning-not-allowed')); return false; } if (Subscriptions.findOne({ rid: message.rid }) == null) { toastr.error(TAPi18n.__('error-pinning-message')); return false; } toastr.success(TAPi18n.__('Message_has_been_pinned')); return ChatMessage.update({ _id: message._id, }, { $set: { pinned: true, }, }); }, // ... }); ``` The Meteor.method `pinMessage` accepts a message object as input with `_id` and `rid` keys. With a known Message ID and any Room ID that is accessible by the attacker, the check room subscriptions can be circumvented, because the target chat message is not validated to be in the same room as validated with `Subscriptions.findOne({ rid: message.rid }`. In addition to that the `pinMessage` function accepts JavaScript objects that are then directly forwarded to the MongoDB model, allowing attackers to use regular expressions to improve guessing of message IDs. ```javascript Meteor.call("pinMessage", { _id: { $regex: /.*/ }, rid: "<ACCESSIBLE_ROOM_ID>" }, (...args) => console.log(...args)); ``` The Meteor.call return data contains the message content, so that an arbitrary user with access to any channel can leak individual messages outside of their accessible channels. ## Releases Affected: * 3.9.10 / develop ## Steps To Reproduce (from initial installation to vulnerability): (Add details for how we can reproduce the issue) 1. Open Rocket.Chat 2. Find any accessible Room ID (for instance from channel avatar URL) 3. Open Web Inspector 4. Execute pinMessage Meteor.call and receive message content in return callback ## Suggested mitigation * Check message object data types * Query the target `rid` along with the updated message `_id`. ```diff diff --git a/app/message-pin/client/pinMessage.js b/app/message-pin/client/pinMessage.js index 9fbc2f778..c360c5d9c 100644 --- a/app/message-pin/client/pinMessage.js +++ b/app/message-pin/client/pinMessage.js @@ -1,4 +1,5 @@ import { Meteor } from 'meteor/meteor'; +import { check } from 'meteor/check'; import toastr from 'toastr'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; @@ -7,6 +8,8 @@ import { ChatMessage, Subscriptions } from '../../models'; Meteor.methods({ pinMessage(message) { + check(message._id, String); + check(message.rid, String); if (!Meteor.userId()) { toastr.error(TAPi18n.__('error-not-authorized')); return false; @@ -22,6 +25,7 @@ Meteor.methods({ toastr.success(TAPi18n.__('Message_has_been_pinned')); return ChatMessage.update({ _id: message._id, + rid: message.rid }, { $set: { pinned: true, @@ -29,6 +33,8 @@ Meteor.methods({ }); }, unpinMessage(message) { + check(message._id, String); + check(message.rid, String); if (!Meteor.userId()) { toastr.error(TAPi18n.__('error-not-authorized')); return false; @@ -44,6 +50,7 @@ Meteor.methods({ toastr.success(TAPi18n.__('Message_has_been_unpinned')); return ChatMessage.update({ _id: message._id, + rid: message.rid }, { $set: { pinned: false, ``` ## Impact Content of arbitrary (private) messages can be leaked by any client with access to at least one room.

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Submitted

Weakness

Information Disclosure