Summary
Lemur: Sub-CA creation never checks AuthorityPermission on the parent authority
Repo under test: https://github.com/Netflix/lemur
When ADMIN_ONLY_AUTHORITY_CREATION=False (an explicitly supported and documented configuration), POST /api/1/authorities with type=subca never verifies that the caller holds AuthorityPermission on the supplied parent authority. The parent field is resolved by AssociatedAuthoritySchema via a raw fetch_objects(Authority, data) lookup, then passed straight through service.create → mint → cryptography-issuer.create_authority, which loads options["parent"].authority_certificate.private_key and signs a brand-new intermediate CA on the caller's behalf.
Any authenticated non-read-only user can therefore mint a sub-CA chained to any internal root whose private key Lemur holds, including roots they hold no role on, attach a role they already belong to, and immediately issue or offline-sign trusted leaf certificates for arbitrary names.
Affected route
POST /api/1/authorities (with type=subca)
Affected code
lemur/authorities/views.py:231, gates only onAuthorityCreatorPermission()+StrictRolePermission(); noAuthorityPermissionondata['parent']lemur/authorities/schemas.py:58,parent = fields.Nested(AssociatedAuthoritySchema);validate_subcaonly checks presencelemur/schemas.py:107,AssociatedAuthoritySchema.get_object→fetch_objects(Authority, data)resolves any authority by id/name with no permission checklemur/plugins/lemur_cryptography/plugin.py:40,private_key = options["authority"].authority_certificate.private_key(set fromoptions["parent"]) signs the new intermediatelemur/auth/permissions.py:62,AuthorityCreatorPermissionbecomes always-allow whenADMIN_ONLY_AUTHORITY_CREATION=Falselemur/certificates/views.py:538,is_private_authority(true forcryptography-issuer) skipsUSER_DOMAIN_AUTHORIZATION_PROVIDERfor subsequent leaf issuance
Root cause
AuthoritiesList.post evaluates AuthorityCreatorPermission (a global "may create authorities" flag) and StrictRolePermission, but never evaluates AuthorityPermission(parent.id, parent.roles) against the caller-supplied parent. AssociatedAuthoritySchema is a pure lookup schema with no authz hook, and neither authorities.service.create nor mint re-check before invoking issuer_plugin.create_authority(options). The bundled cryptography-issuer then uses the parent's stored private key directly.
Validated evidence
Static trace, confirmed by code inspection (validation status: CONFIRMED):
parentis loaded viaAssociatedAuthoritySchema(rawfetch_objects), passed unchecked throughviews.post → service.create → mint → plugin.create_authority → issue_certificate, where the parent authority's stored private key is read and used to sign the new intermediate.- No call to
AuthorityPermission(parent.id, ...)exists anywhere on this path. - Precondition
ADMIN_ONLY_AUTHORITY_CREATION=Falseis an explicitly supported config (docs/administration.rst:517).
Proof of concept / reproducer
Status: reconstructed from source report (static control-flow trace; not executed against a live CA).
Preconditions: ADMIN_ONLY_AUTHORITY_CREATION=False; attacker is an authenticated Lemur user holding any role other than read-only; <PARENT_AUTHORITY_ID> is any internal cryptography-issuer root CA the attacker holds no role on; <ATTACKER_ROLE> is any role the attacker already belongs to.
curl -sS -X POST "<TARGET_BASE_URL>/api/1/authorities" \
-H "Authorization: Bearer <AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "attacker-subca",
"owner": "[email protected]",
"description": "poc",
"type": "subca",
"parent": {"id": <PARENT_AUTHORITY_ID>},
"plugin": {"slug": "cryptography-issuer"},
"roles": [{"name": "<ATTACKER_ROLE>"}],
"commonName": "attacker-intermediate",
"validityYears": 1
}'
The response contains a new authority whose authority_certificate is signed by <PARENT_AUTHORITY_ID>'s private key. The caller is recorded as creator and holds <ATTACKER_ROLE> on it, so POST /api/1/certificates against the new authority succeeds (and skips allowed_issuance_for_domain because is_private_authority is true).
Static-trace validation command from the source report:
grep -n 'parent' lemur/authorities/schemas.py lemur/authorities/views.py lemur/authorities/service.py \
&& sed -n '37,55p' lemur/plugins/lemur_cryptography/plugin.py
Source artifact: audit/harnesses/public-repo-threat-model-harness/results/netflix-lemur-100run-mythos-20260627T051129Z/findings.jsonl (run_022, finding cluster lemur-subca-parent-authz, 5/100 runs).
Impact
In deployments that set ADMIN_ONLY_AUTHORITY_CREATION=False to enable self-service CA creation, any authenticated non-read-only user, with zero permission on a given internal root CA, can obtain a working intermediate CA chained to that root. They can then:
- Issue TLS certificates for arbitrary names trusted by every relying party that trusts the internal root, bypassing
LEMUR_ALLOWED_DOMAINS, sensitive-domain flags, and the per-user domain-authorization plugin. - Export the sub-CA private key and sign end-entity certificates entirely outside Lemur, defeating all in-product issuance controls.
This converts "can create a self-contained test CA" into "can mint trusted certs under any internal PKI root in the organisation". The ADMIN_ONLY_AUTHORITY_CREATION documentation does not warn operators of this consequence.
The application does not perform an authorization check before performing a sensitive operation. Typical impact: unauthorized access to restricted functionality or data.
CVE-2026-71317 has a CVSS score of 6.5 (Medium). The vector is requires local access, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.9.3); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
In AuthoritiesList.post (or authorities.service.create), when data.get('parent') is present, enforce AuthorityPermission(parent.id, [r.name for r in parent.roles]).can() before invoking the issuer plugin, regardless of ADMIN_ONLY_AUTHORITY_CREATION. Additionally, update the ADMIN_ONLY_AUTHORITY_CREATION documentation to state that disabling it currently grants every authenticated user the ability to chain sub-CAs off any internal root whose private key Lemur holds. Consider also requiring admin (or an explicit per-parent capability) for any type=subca creation independent of the global flag.
Frequently Asked Questions
- What is CVE-2026-71317? CVE-2026-71317 is a medium-severity missing authorization vulnerability in lemur (pip), affecting versions <= 1.9.2. It is fixed in 1.9.3. The application does not perform an authorization check before performing a sensitive operation.
- How severe is CVE-2026-71317? CVE-2026-71317 has a CVSS score of 6.5 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of lemur are affected by CVE-2026-71317? lemur (pip) versions <= 1.9.2 is affected.
- Is there a fix for CVE-2026-71317? Yes. CVE-2026-71317 is fixed in 1.9.3. Upgrade to this version or later.
- Is CVE-2026-71317 exploitable, and should I be worried? Whether CVE-2026-71317 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-71317 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-71317? Upgrade
lemurto 1.9.3 or later.