Lemur Privilege Escalation: Non-admin role members can rewrite role membership via PUT /api/1/roles/<id>
Date: 2026-06
Affected: pip/lemur
Attack vector: other
Category: vulnerability-disclosure
Severity: Medium
CVE: CVE-2026-55162
CVE-2026-55163
CWE: CWE-863
CWE-918
CVSS: 6.3 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L)

## Summary The `PUT /api/1/roles/<id>` handler in `lemur/roles/views.py` gates only on `RoleMemberPermission(role_id).can()`, which is satisfied for any user who is already a member of the target role. The handler then passes `data["users"]` and `data["name"]` directly to `service.update()`, permitting any role member to rewrite that role's membership list and name. The companion `DELETE` handler on the same resource is correctly gated by `@admin_permission.require`; the asymmetry between PUT and DELETE on identical resources indicates an authorization oversight rather than a deliberate design choice. ## Root Cause `lemur/roles/views.py:298`: ```python permission = RoleMemberPermission(role_id) if permission.can(): return service.update( role_id, data["name"], data.get("description"), data.get("users") ) return dict(message="You are not authorized to modify this role."), 403 @admin_permission.require(http_exception=403) def delete(self, role_id): ... ``` `lemur/auth/permissions.py:56`: ```python class RoleMemberPermission(Permission): def __init__(self, role_id): needs = [RoleNeed("admin"), RoleMemberNeed(role_id)] super().__init__(*needs) ``` `flask_principal.Permission.allows()` is OR-semantic across needs, so `RoleMemberPermission(role_id).can()` returns `True` if the caller is either an admin **or** a member of `role_id`. The PUT handler treats membership-of-self as sufficient to mutate the role; DELETE does not. #


Mitigations:

Architectural layers (MAESTRO):