getUsersOfRoom discloses users in private channels
Team Summary
Official summary from Rocket.Chat
## Summary Improper input data validation in the `getUsersOfRoom` Meteor server method allows authenticated users to enumerate existing rooms and subscribed users. ## Description Input data in the `getUsersOfRoom` Meteor server method is not type validated, so that MongoDB query operator objects are accepted by the server, so that instead of a matching rid String a`$regex` query can be executed, bypassing the room access permssion check for every but the first matching room. When the user-provided `rid` method argument is a MongoDB query operator object, the first match will be used to check the requesting users access permissions against ([server/methods/getUsersOfRoom.js#L18-L21](https://github.com/RocketChat/Rocket.Chat/blob/194a600f31a1037716ac4de297cfff0b8a4f9942/server/methods/getUsersOfRoom.js#L18-L21)): ```javascript const room = Rooms.findOneById(rid, { fields: { broadcast: 1 } }); if (!room) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'getUsersOfRoom' }); } if (!canAccessRoom(room, { _id: userId })) { throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'getUsersOfRoom' }); } ``` The original `rid`is later lassed to the `findUsersOfRoom` method ([server/methods/getUsersOfRoom.js#L33-L38](https://github.com/RocketChat/Rocket.Chat/blob/194a600f31a1037716ac4de297cfff0b8a4f9942/server/methods/getUsersOfRoom.js#L33-L38): ```javascript const users = findUsersOfRoom({ rid, status: !showAll ? { $ne: 'offline' } : undefined, limit, skip, filter }).fetch(); return { total, records: users, }; ``` The `rid` parameter is then passed unchanged in [](https://github.com/RocketChat/Rocket.Chat/blob/194a600f31a1037716ac4de297cfff0b8a4f9942/server/lib/findUsersOfRoom.ts#L34-L37) to [Users.findByActiveUsersExcept](https://github.com/RocketChat/Rocket.Chat/blob/194a600f31a1037716ac4de297cfff0b8a4f9942/app/models/server/models/Users.js#L797) ```javascript return Users.findByActiveUsersExcept(filter, undefined, options, undefined, [{ __rooms: rid, ...status && { status }, }]); ``` In the `Users.findByActiveUsersExcept` function the `extraQuery` object (containing the user-provided `rid` argument ) queries directly from MongoDB ([app/models/server/models/Users.js#L828-L840](https://github.com/RocketChat/Rocket.Chat/blob/194a600f31a1037716ac4de297cfff0b8a4f9942/app/models/server/models/Users.js#L828-L840])): ```javascript findByActiveUsersExcept(searchTerm, exceptions, options, forcedSearchFields, extraQuery = [], { startsWith = false, endsWith = false } = {}) { // ... const query = { $and: [ { active: true, username: { $exists: true, $nin: exceptions }, $or: orStmt, }, ...extraQuery, ], }; // do not use cache return this._db.find(query, options); } ``` Because the MongoDB `find()` query can return matches for multiple rooms but the `Rooms.findOneById` lookup only matches the first, all but the first matching rooms will be accepted without further permission check. ```javascript const ACCESSIBLE_CHANEL="<ROOM_ID>"; const TARGET_CHANNEL="<ROOM_ID_REGEX>"; // .* to get all users in any room Meteor.call( "getUsersOfRoom", { $regex: `(${OWN_CHANNEL}|${TARGET_CHANNEL)" }, // rid true , // showAll console.log ); ``` ## Releases Affected: * [develop](https://github.com/RocketChat/Rocket.Chat/tree/194a600f31a1037716ac4de297cfff0b8a4f9942) * 4.1.1 [672fe95d7e8afbd7d306cf176f54c65dd9be0eea](https://github.com/RocketChat/Rocket.Chat/commit/672fe95d7e8afbd7d306cf176f54c65dd9be0eea) * 4.1.2 ## Steps To Reproduce (from initial installation to vulnerability): 1. Login to Rocket.Chat 2. Create empty room and find Room ID 3. Call `getUsersOfRoom` Meteor method with `{ $regex: "(<MY_ROOM_ID>|<TARGET_ROOM_ID>" }` 4. Receive list of all users from both channels ## Suggested mitigation * Validate `rid` argument to be a String ## Impact Users with access to at least one channel can leak the members of another channel they should not have access to. ## Fix Fixed in 4.7.5, 4.8.2 and 5.0.0
Report Details
Additional information and metadata
State
Closed
Substate
Resolved