CVE-2017-0881 (published 2017-03-28T02:59Z)

An error in the implementation of an autosubscribe feature in the check_stream_exists route of the Zulip group chat application server before 1.4.3 allowed an authenticated user to subscribe to a private stream that should have required an invitation from an existing member to join. The issue affects all previously released versions of the Zulip server.

Severity: MEDIUM
CVSS v2 base score: 4.0 (AV:N/AC:L/Au:S/C:P/I:N/A:N)
CVSS v3 base score: 4.3, MEDIUM (CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-863
cwe_name: Incorrect Authorization
description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are incorrectly applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/zulip/zulip
commit_details: repo_url: https://github.com/zulip/zulip
author: Tim Abbott
author_date: 2017-01-22 20:22:40-08:00
msg: streams: Fix autosubscribe security bug (CVE-2017-0881).

A bug in Zulip's implementation of the "stream exists" endpoint meant
that any user of a Zulip server could subscribe to an invite-only
stream without needing to be invited by using the "autosubscribe"
argument.

Thanks to Rafid Aslam for discovering this issue.
repository: repo_url: https://github.com/zulip/zulip
repo_name: zulip/zulip
description: Zulip server and web application. Open-source team chat that helps teams stay productive and focused.
repo_language: Python
owner: zulip
file_changes: filename: test_subs.py
change_type: ModificationType.MODIFY
diff: @@ -1936,6 +1936,29 @@ class SubscriptionAPITest(ZulipTestCase):
         self.assertIn("exists", json)
         self.assertTrue(json["exists"])
 
+    def test_existing_subscriptions_autosubscription_private_stream(self):
+        # type: () -> None
+        """Call /json/subscriptions/exist on an existing private stream with
+        autosubscribe should fail.
+        """
+        stream_name = "Saxony"
+        result = self.common_subscribe_to_streams("cordelia@zulip.com", [stream_name],
+                                                  invite_only=True)
+        stream = get_stream(stream_name, self.realm)
+
+        result = self.client_post("/json/subscriptions/exists",
+                                  {"stream": stream_name, "autosubscribe": True})
+        self.assert_json_success(result)
+        json = ujson.loads(result.content)
+        self.assertIn("exists", json)
+        self.assertTrue(json["exists"])
+        self.assertIn("subscribed", json)
+        # Importantly, we are not now subscribed
+        self.assertFalse(json["subscribed"])
+        self.assertEqual(Subscription.objects.filter(
+            recipient__type=Recipient.STREAM,
+            recipient__type_id=stream.id).count(), 1)
+
     def get_subscription(self, user_profile, stream_name):
         # type: (UserProfile, Text) -> Subscription
         stream = get_stream(stream_name, self.realm)

programming_language: Python
filename: streams.py
change_type: ModificationType.MODIFY
diff: @@ -481,7 +481,7 @@ def stream_exists_backend(request, user_profile, stream_id, autosubscribe):
     result = {"exists": bool(stream)}
     if stream is not None:
         recipient = get_recipient(Recipient.STREAM, stream.id)
-        if autosubscribe:
+        if not stream.invite_only and autosubscribe:
             bulk_add_subscriptions([stream], [user_profile])
         result["subscribed"] = is_active_subscriber(
             user_profile=user_profile,

programming_language: Python