4.3 Medium
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
LOW
Integrity Impact
NONE
Availability Impact
NONE
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
4 Medium
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
SINGLE
Confidentiality Impact
PARTIAL
Integrity Impact
NONE
Availability Impact
NONE
AV:N/AC:L/Au:S/C:P/I:N/A:N
0.0004 Low
EPSS
Percentile
12.0%
Improper input data validation in the getUsersOfRoom
Meteor server method allows authenticated users to enumerate existing rooms and subscribed users.
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):
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:
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 to Users.findByActiveUsersExcept
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):
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.
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
);
getUsersOfRoom
Meteor method with { $regex: "(<MY_ROOM_ID>|<TARGET_ROOM_ID>" }
rid
argument to be a StringUsers with access to at least one channel can leak the members of another channel they should not have access to.
Fixed in 4.7.5, 4.8.2 and 5.0.0
4.3 Medium
CVSS3
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
LOW
User Interaction
NONE
Scope
UNCHANGED
Confidentiality Impact
LOW
Integrity Impact
NONE
Availability Impact
NONE
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
4 Medium
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
SINGLE
Confidentiality Impact
PARTIAL
Integrity Impact
NONE
Availability Impact
NONE
AV:N/AC:L/Au:S/C:P/I:N/A:N
0.0004 Low
EPSS
Percentile
12.0%