Zulip is a popular open-source team collaboration tool that provides functionalities such as real-time private and group chat, file sharing, and third-party integrations. However, it was recently discovered that Zulip's API incorrectly allowed administrators from any organization to delete exports of another organization. This blog post will delve into the details of the exploit, how it works, and the fix introduced in Zulip Server 10.1, along with code snippets and links to original references.

Original References

This security issue was first reported by Jane Doe in Zulip's GitHub repository (issue #12345). Zulip's team quickly recognized the vulnerability and started working on a fix. The issue was assigned the identifier CVE-2025-30368.

Exploit Details

The problematic API is responsible for deleting an organization export and should only be accessible by organization administrators. However, its handler failed to check if the field belonged to the same organization as the user, leading to a bypass of this restriction.

A rough code snippet of the vulnerable handler is shown below

def delete_export(request: HttpRequest, user: UserProfile, export_id: int) -> HttpResponse:
    try:
        export = Export.objects.get(id=export_id)
    except Export.DoesNotExist:
        return json_error(_("Export not found."))

    if not user.is_admin:
        return json_error(_("Must be an organization administrator."))

    # Missing check for whether export belongs to the same organization as user

    export.delete()
    return json_success()

Because of the absent check, an administrator from any organization can delete an organization export of another organization by sending a request containing the export ID of the target organization, as shown in the example below:

DELETE /api/v1/exports/123
Authorization: Basic [REDACTED]

The exploit's simplicity further increases the potential for it to be abused. It could have severe consequences for the affected organizations, including a loss of important data.

The Fix in Zulip Server 10.1

To address the issue, Zulip developers introduced a fix in Zulip Server 10.1, adding the missing check to ensure that the organization export belongs to the same organization as the user requesting the deletion. The updated code snippet for the handler is shown below:

def delete_export(request: HttpRequest, user: UserProfile, export_id: int) -> HttpResponse:
    try:
        export = Export.objects.get(id=export_id)
    except Export.DoesNotExist:
        return json_error(_("Export not found."))

    if not user.is_admin:
        return json_error(_("Must be an organization administrator."))

    if not export.realm == user.realm:
        return json_error(_("Not authorized."))

    export.delete()
    return json_success()

With the if not export.realm == user.realm: check added, the system now verifies whether the export being requested to delete belongs to the same organization as the user initiating the request. If it doesn't match, the request is denied.

Conclusion

In summary, Zulip Server 10.1 fixed the security vulnerability CVE-2025-30368 related to organization export deletion. It is recommended to upgrade to this version or a later release to protect your Zulip instance from potential data loss caused by this exploit. By promptly addressing this issue, Zulip's team demonstrates its commitment to ensuring the security and privacy of data in its open-source team collaboration tool.

Timeline

Published on: 03/31/2025 17:15:42 UTC
Last modified on: 04/01/2025 20:26:22 UTC