HexBrain

Nextcloud · Report #3636758

Cross-room signaling injection enabling unauthorized force-mute

Validstatus
lowseverity
12upvotes
21 Oct 2026disclosed

How it was found

Summary:

A low-privileged participant can read another participant's live Talk sessionId from the room participant list, then use moderator rights in an unrelated room they control to inject moderator-only internal-signaling control messages into the victim's room session.

The server checks moderator permissions only against the sender session's own room, but it queues the message to any attacker-supplied recipient sessionId. Message delivery is then keyed only by recipient session ID, not by room. I reproduced this by sending a forceMute control from room B into a victim active in room A.

When the victim is in a call, the shipped client handles forceMute by muting the local user session.

Product / Version

  • Nextcloud Server: 34.0.0.1 (status.php reported versionstring: 34.0.0 dev)
  • Talk app: 24.0.0-dev.2
  • spreed commit: 031e0708bde280b2a83943a0e7a6102d8eede207
  • Server commit used in the lab: b71661a38bb40fbdfc071f273ea506a4a9fae091
  • Reproduced with the default internal-signaling mode on a local Nextcloud lab

Affected Code

  • lib/Controller/RoomController.php:1217-1220 Active participant session IDs are exposed in the participant list.
  • lib/Controller/RoomController.php:1250-1253 Single-session participants are returned with sessionIds.
  • lib/Controller/SignalingController.php:387-418 control messages are authorized against the sender's session, then queued to arbitrary decodedMessage['to'].
  • lib/Signaling/Messages.php:48-56 Queue entries store sender, recipient, and message only.
  • lib/Signaling/Messages.php:102-110 Messages are fetched solely by recipient = $sessionId.
  • lib/Controller/SignalingController.php:551-585 Victim polling consumes any queued message for that session.
  • src/utils/webrtc/simplewebrtc/simplewebrtc.js:108-113 Incoming forceMute causes the client to mute itself when peerId matches the current session.

Steps To Reproduce:

Preconditions:

  • Victim and attacker both participate in room A.
  • Attacker is only a normal participant in room A.
  • Attacker owns or moderates unrelated room B.
  • Internal signaling is enabled.

Steps:

  1. On nextcloud spreed (34.0.0.1 / Talk 24.0.0-dev.2), victim created room A and added attacker (evil). Both joined room A. evil queried GET /ocs/v2.php/apps/spreed/api/v4/room/97oqymwq/participants?format=json and received victim’s live room-A session ID JRal9O65....

  2. evil created unrelated room B, joined it, and got room-B sender session pekgCNiV....

  3. evil sent this to room B:

    { "messages": "[{"ev":"message","sessionId":"<evil_room_b_session>","fn":"{\"type\":\"control\",\"to\":\"<victim_room_a_session>\",\"payload\":{\"action\":\"forceMute\",\"peerId\":\"<victim_room_a_session>\"}}"}]" } to POST /ocs/v2.php/apps/spreed/api/v3/signaling/pfo7kdcz?format=json

  4. Server-side evidence showed a queued row with sender pekgCNiV... and recipient JRal9O65... in oc_talk_internalsignaling.

  5. victim then polled room A with GET /ocs/v2.php/apps/spreed/api/v3/signaling/97oqymwq?format=json and received the injected control message with "from":"pekgCNiV...".

  6. Negative control: sending the same forceMute from room A itself, using evil’s room-A session QBL3zUuH..., created no victim-targeted queue row and victim’s next poll returned only usersInRoom.

Supporting Material/References:

Here the attacker reads the session of the victim in the shared room {F5634941}

the attacker uses the victim's session as the recipient and the attackers room B as the sender {F5634940}

victim in room A receives injected message from attacker in room B and is muted {F5634942}

Impact

  • A low-privileged user can inject moderator-only internal-signaling controls into a room they do not moderate.
  • The reproduced payload is forceMute. When the victim is in a call, the shipped client handles this by muting the local session.
  • More generally, the room boundary for internal signaling is broken because delivery is keyed only by recipient session ID.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original