# Lemur has an authorization bypass in StrictRolePermission / AuthorityCreatorPermission

Lemur's StrictRolePermission and AuthorityCreatorPermission built empty Need sets by default, so any authenticated read-only user could create root CAs and upload certificates.

- Payout: Research
- Severity: high
- Program: Lemur
- Found by: hits313
- Disclosed: 2026-06-25
- CVE: CVE-2026-48508
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-qcqw-jwxc-2hqg
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-qcqw-jwxc-2hqg
- Tags: area:web domain:api domain:web impact:integrity impact:privesc kind:research platform:ghsa sev:high tech:python technique:source-review vuln:access-control vuln:auth-bypass vuln:misconfiguration

**Target:** Flask-Principal permission classes on Lemur REST API endpoints
**Root cause:** Permission subclasses called super().__init__() with no Needs when config flags defaulted to False; flask_principal.Permission.allows() returns True for empty needs, so .can() passed for every authenticated identity.
**Steps:**
1. Authenticate as a user with only the `read-only` role.
2. Send `POST /api/1/authorities` to create a root CA; both AuthorityCreatorPermission and StrictRolePermission have empty Needs so the gate passes.
3. Alternatively `POST /api/1/certificates/upload`, `POST /api/1/notifications`, or `POST /api/1/domains` — each gated only by StrictRolePermission().can().
4. Observe the action succeeds despite lacking admin/operator role.
**How found:** Code review of lemur/auth/permissions.py: noticed both permission classes call super().__init__() with no Needs in the else branch, then traced flask_principal.Permission.allows() to confirm empty needs short-circuits the guard to True, and enumerated views whose only gate is these permissions.
**Exploitation:** Any authenticated low-privilege identity (e.g. phished employee or leaked SSO token) can create root CAs with attacker-owned keys persisted in Lemur's DB, upload arbitrary certs/keys to destinations like AWS push, create notifications reaching an SSRF sink, and mark domains sensitive to manipulate issuance approval.
**Escalation:** Pivot from any authenticated identity to control of the PKI issuance plane; attacker-owned root CA can sign against internal trust stores pinning Lemur-managed roots.
**Insight:** When a permission class conditionally builds its Need set from a config flag, check the default and the empty-set branch: Flask-Principal treats empty needs as allow-all, so a permissive default silently disables authorization on every endpoint gated solely by that class.

