AI analysis
Lemonldap::NG::Portal versions 2.23.0 through 2.23.3 fail to enforce PKCE for public OpenID Connect Relying Parties configured in 'PKCE or secret' mode (oidcRPMetaDataOptionsRequirePKCE = 2). Because checkEndPointAuthenticationCredentials() skips client-secret verification for public clients, an attacker who intercepts an authorization code can replay the client_id with any arbitrary secret at the token endpoint and exchange it for the victim's access, ID, and refresh tokens — exactly the attack PKCE is designed to stop. The authorization endpoint also issues codes without any code_challenge, and validatePKCEChallenge() passes when neither challenge nor verifier is present. Any organization running the 2.23.x branch as an OIDC provider is affected, with higher risk where dynamic client registration is enabled, since it creates every Relying Party in this mode. No CVSS score has been assigned, there is no known public PoC, and no exploitation has been observed in the wild.
What to do: Upgrade Lemonldap::NG::Portal to version 2.23.4 or later. Until patched, avoid setting oidcRPMetaDataOptionsRequirePKCE to 2 ('PKCE or secret') for public Relying Parties and require strict PKCE instead, and disable or audit dynamic client registration since it creates clients in the vulnerable mode. Review token endpoint logs for exchanges where a public client_id authenticated with a secret or where no code_verifier was presented, and rotate refresh tokens for affected Relying Parties if suspicious activity is found.
Affected
| Lemonldap::NG::Portal (Perl) | >= 2.23.0, < 2.23.4 |
Estimated exposure
nicheLikely on the order of a few hundred to low thousands of deployments; exact count unknown — Lemonldap::NG is a self-hosted, open-source SSO/IAM product with no public install telemetry, concentrated in European (especially French) public-sector and enterprise deployments, and only the recent 2.23.0–2.23.3 branch is affected,…
Description
Lemonldap::NG::Portal versions from 2.23.0 before 2.23.4 for Perl allow a PKCE bypass for public Relying Parties in "PKCE or secret" mode because checkEndPointAuthenticationCredentials does not verify the client secret. With oidcRPMetaDataOptionsRequirePKCE set to 2, the authorization endpoint issues a code even when the request carries no code_challenge, and token() admits the exchange as long as a challenge was stored or an authentication method was returned for the caller. checkEndPointAuthenticationCredentials() skips the secret comparison for a Relying Party marked public and still returns the method deduced from the request, so any Basic or form credential satisfies the secret branch. validatePKCEChallenge() then passes, because neither a challenge nor a verifier is present. An attacker who intercepts an authorization code issued to a public Relying Party can exchange it for the user's access, ID and refresh tokens by replaying the client_id with an arbitrary secret, which is the attack PKCE prevents. Dynamic client registration creates every Relying Party in this mode.