Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2025-47930

CVE-2025-47930: Zulip Authorization Bypass Vulnerability

CVE-2025-47930 is an authorization bypass flaw in Zulip that allows users to circumvent channel creation access controls by exploiting privacy setting changes. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2025-47930 Overview

CVE-2025-47930 is an authorization bypass vulnerability in Zulip, an open-source team chat application. The flaw affects Zulip versions 10.0 through 10.2 and allows authenticated users to circumvent the "Who can create public channels" access control. An attacker with an ordinary user account can create a private or web-public channel, then modify its privacy setting to public. A similar technique bypasses restrictions on creating private channels, though it requires direct API calls or HTML manipulation because the client marks the private radio button as disabled. Zulip version 10.3 contains the patch. The weakness is categorized under CWE-863: Incorrect Authorization.

Critical Impact

Authenticated users can create channels of any privacy type regardless of organization-configured permissions, undermining administrative access controls.

Affected Products

  • Zulip Server 10.0
  • Zulip Server 10.1
  • Zulip Server 10.2

Discovery Timeline

  • 2025-05-16 - CVE-2025-47930 published to NVD
  • 2026-06-17 - Last updated in NVD database

Technical Details for CVE-2025-47930

Vulnerability Analysis

The vulnerability resides in the channel update logic in zerver/views/streams.py. Zulip enforces permission checks on the channel creation endpoint, verifying whether a user holds the can_create_public_streams or can_create_private_streams role. However, the channel update endpoint did not perform the equivalent check when a user changed the privacy setting of an existing channel.

An attacker with permission to create at least one channel type can chain two operations. First, they create a channel of a permitted privacy level. Second, they issue an update request that toggles the is_private attribute to the restricted level. The absence of a re-check on the update path allows the transition to succeed, effectively producing a channel the user was never authorized to create.

Root Cause

The root cause is missing authorization enforcement on a state-changing operation. The creation flow validated caller privileges, but the update flow trusted that the initial permission check was sufficient. This design assumption fails because channel privacy is a policy-governed attribute rather than an immutable property.

Attack Vector

Exploitation requires network access and a low-privilege authenticated Zulip account within the target organization. No user interaction from an administrator is needed. The public-channel bypass works through the standard web UI, while the private-channel bypass requires API access or client-side modification because the UI disables the private option.

python
         # public/private status for channels requires content access
         # to the channel.
 
+    if is_private is not None:
+        if is_private and not user_profile.can_create_private_streams():
+            raise JsonableError(_("Insufficient permission"))
+
+        if not is_private and not user_profile.can_create_public_streams():
+            raise JsonableError(_("Insufficient permission"))
+
     # Enforce restrictions on creating web-public streams. Since these
     # checks are only required when changing a stream to be
     # web-public, we don't use an "is not None" check.

Source: Zulip security patch commit d2ff4bd. The patch adds explicit calls to can_create_private_streams() and can_create_public_streams() when the update payload includes an is_private change, mirroring the checks already present in the creation path.

Detection Methods for CVE-2025-47930

Indicators of Compromise

  • Channel objects whose privacy setting changed shortly after creation by a user without matching creation permissions.
  • API PATCH requests to /json/streams/{stream_id} containing an is_private parameter from non-privileged accounts.
  • New public channels owned or created by users whose role only permits private channel creation.

Detection Strategies

  • Correlate Zulip audit log entries for channel creation and subsequent privacy updates by the same user within short time windows.
  • Compare each channel privacy transition against the acting user's can_create_public_streams and can_create_private_streams role at the time of the change.
  • Alert on channel update API traffic that toggles is_private when the source account lacks the target creation permission.

Monitoring Recommendations

  • Ingest Zulip server access logs and RealmAuditLog events into a centralized logging or SIEM platform.
  • Baseline expected channel administrators and flag channel privacy changes originating from accounts outside that set.
  • Review web-public channel creation events, since these carry the broadest exposure surface.

How to Mitigate CVE-2025-47930

Immediate Actions Required

  • Upgrade Zulip Server to version 10.3 or later, which contains the patch.
  • Audit existing channels created between the deployment of version 10.0 and the upgrade for unexpected privacy transitions.
  • Review and, where required, tighten the "Who can create public channels" and "Who can create private channels" organization settings.

Patch Information

The fix is delivered in Zulip Server 10.3 via commit d2ff4bd. Full technical details are documented in the GitHub Security Advisory GHSA-rqg7-xfqg-v7q5.

Workarounds

  • Restrict channel creation permissions to trusted administrators using the Zulip channel permission settings until the upgrade is applied.
  • Manually revert unauthorized channel privacy changes identified during log review.
  • Monitor the RealmAuditLog for channel property updates and revoke privileges from accounts observed abusing the bypass.
bash
# Upgrade Zulip Server to the patched release
sudo /home/zulip/deployments/current/scripts/upgrade-zulip-from-git 10.3

# Verify installed version
/home/zulip/deployments/current/manage.py print_initial_password --version

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.