CVE-2026-71307: Lemur Authenticated Plaintext Destination Credential Exposure
Any logged-in Lemur user, including read-only accounts, can call the destinations API and get back plaintext SFTP passwords and private-key passphrases that Lemur uses to deploy TLS certificates.

The problem
Lemur's GET /api/1/destinations and GET /api/1/destinations/<id> endpoints are protected only by login_required, inherited from AuthenticatedResource. No role check is applied.
The sibling write handlers (POST, PUT, DELETE) all carry @admin_permission.require(http_exception=403), but the two read handlers do not. DestinationOutputSchema serializes every stored option verbatim, including the password and privateKeyPass fields that the built-in SFTP plugin explicitly stores as cleartext.
Any authenticated principal, including users intentionally restricted to the read-only role, can enumerate all configured destinations and harvest those secrets.
Proof of concept
A working proof-of-concept for CVE-2026-71307 in lemur, with the exact payload below.
# Step 1: obtain a JWT for any low-privilege or read-only Lemur account
# Step 2: call either read endpoint
GET /api/1/destinations/4 HTTP/1.1
Host: lemur.example.com
Authorization: Bearer <low-priv-user-token>
# Response (200 OK) -- secrets appear in both top-level options
# and plugin.pluginOptions, exactly as stored:
{
"id": 4,
"label": "prod-nginx-sftp",
"options": [
{"name": "host", "type": "str", "value": "10.0.5.20"},
{"name": "user", "type": "str", "value": "deploy"},
{"name": "password", "type": "str", "value": "S3cr3t-SFTP-Passw0rd!"},
{"name": "privateKeyPass", "type": "str", "value": "rsa-key-passphrase-xyz"}
],
"plugin": {
"slug": "sftp-destination",
"pluginOptions": [
{"name": "password", "value": "S3cr3t-SFTP-Passw0rd!"},
{"name": "privateKeyPass", "value": "rsa-key-passphrase-xyz"}
]
}
}
# GET /api/1/destinations (no id) returns the same fields for every
# configured destination in one call.The root cause is a missing authorization check (CWE-862). DestinationsList.get() and Destinations.get() inherit only login_required from AuthenticatedResource; neither calls admin_permission.require() or any equivalent role gate.
DestinationOutputSchema serializes the raw options JSONType column with fields.List(fields.Dict()) and then copies it verbatim into plugin.pluginOptions via a @post_dump hook. The SFTP plugin itself documents that its password and privateKeyPass fields are stored as plaintext, so the schema leak is complete.
Patch commit 751c970 adds value redaction inside DestinationOutputSchema for callers who do not hold admin_permission, replacing secret option values with a placeholder before the response is serialized. Admins continue to receive the real values so they can edit them.
The fix
Upgrade to Lemur 1.9.3 or later. The patch redacts sensitive destination option values for any non-admin caller at the schema layer. As an interim workaround, restrict network-level access to the Lemur API to admin users only, or temporarily block GET /api/1/destinations* for non-admin principals at your reverse proxy.
Related research
- high · 8.1CVE-2026-71308CVE-2026-71308: Lemur Unauthorized Certificate Hijack via Unchecked replaces Field
- high · 7.4CVE-2026-70666CVE-2026-70666: Lemur ACME Client Server-Side Request Forgery via Server-Controlled URLs
- high · 7.7CVE-2026-71303CVE-2026-71303: Lemur ACME Authority Update SSRF (Incomplete Fix)
- high · 7.3CVE-2026-71417CVE-2026-71417: Lemur Authorization Bypass via Duplicate Certificate Upload Enables Arbitrary CA Revocation