CVE-2017-0896: Missing Authorization (CWE-862)
value: Zulip Server 1.5.1 and below suffer from an error in the implementation of the invite_by_admins_only setting in the Zulip group chat application server that allowed an authenticated user to invite other users to join a Zulip organization even if the organization was configured to prevent this.
CWE description: The product does not perform an authorization check when an actor attempts to access a resource or perform an action.
CVSS v3 base score: 6.5
Published: 2017-06-02T17:29Z
Repository: https://github.com/zulip/zulip
Language: Python
Files changed: zerver/tests/test_signup.py, zerver/views/invite.py
Commit date: 2017-06-01 17:24:29 -0700
Commit message: invite: Fix invite_by_admins_only to be enforced in backend.

This is CVE-2017-0896.

Apparently, this setting never actually was wired up to anything other
than hiding the UI widget.

Huge thanks to Ibram Marzouk from the HackerOne community for finding
this security bug.

Diff: diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py
index 311f9f20f..8b80e7625 100644
--- a/zerver/tests/test_signup.py
+++ b/zerver/tests/test_signup.py
@@ -421,10 +421,33 @@ class InviteUserTest(ZulipTestCase):
         self.assert_json_success(self.invite(invitee, ["Denmark"]))
         self.assertTrue(find_key_by_email(email))
         self.assertTrue(find_key_by_email(email2))
         self.check_sent_emails([email, email2])
 
+    def test_require_realm_admin(self):
+        # type: () -> None
+        """
+        The invite_by_admins_only realm setting works properly.
+        """
+        realm = get_realm('zulip')
+        realm.invite_by_admins_only = True
+        realm.save()
+
+        self.login("hamlet@zulip.com")
+        email = "alice-test@zulip.com"
+        email2 = "bob-test@zulip.com"
+        invitee = "Alice Test <{}>, {}".format(email, email2)
+        self.assert_json_error(self.invite(invitee, ["Denmark"]),
+                               "Must be a realm administrator")
+
+        # Now verify an administrator can do it
+        self.login("iago@zulip.com")
+        self.assert_json_success(self.invite(invitee, ["Denmark"]))
+        self.assertTrue(find_key_by_email(email))
+        self.assertTrue(find_key_by_email(email2))
+        self.check_sent_emails([email, email2])
+
     def test_successful_invite_user_with_notifications_stream(self):
         # type: () -> None
         """
         A call to /json/invite_users with valid parameters unconditionally
         subscribes the invitee to the notifications stream if it exists and is
diff --git a/zerver/views/invite.py b/zerver/views/invite.py
index 769268e7a..5df65ecd5 100644
--- a/zerver/views/invite.py
+++ b/zerver/views/invite.py
@@ -21,10 +21,12 @@ import re
 @has_request_variables
 def json_invite_users(request, user_profile,
                       invitee_emails_raw=REQ("invitee_emails"),
                       body=REQ("custom_body", default=None)):
     # type: (HttpRequest, UserProfile, str, Optional[str]) -> HttpResponse
+    if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
+        return json_error(_("Must be a realm administrator"))
     if not invitee_emails_raw:
         return json_error(_("You must specify at least one email address."))
     if body == '':
         body = None
 

Fixed code:     def test_require_realm_admin(self):
        # type: () -> None
        """
        The invite_by_admins_only realm setting works properly.
        """
        realm = get_realm('zulip')
        realm.invite_by_admins_only = True
        realm.save()

        self.login("hamlet@zulip.com")
        email = "alice-test@zulip.com"
        email2 = "bob-test@zulip.com"
        invitee = "Alice Test <{}>, {}".format(email, email2)
        self.assert_json_error(self.invite(invitee, ["Denmark"]),
                               "Must be a realm administrator")

        # Now verify an administrator can do it
        self.login("iago@zulip.com")
        self.assert_json_success(self.invite(invitee, ["Denmark"]))
        self.assertTrue(find_key_by_email(email))
        self.assertTrue(find_key_by_email(email2))
        self.check_sent_emails([email, email2])

    if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
        return json_error(_("Must be a realm administrator"))