AI analysis
Lemonldap::NG::Portal fails to authenticate callers of its OAuth2 token introspection endpoint when the request names a Relying Party marked as public. The checkEndPointAuthenticationCredentials() routine skips the client secret comparison for public RPs yet still reports an authentication method (client_secret_basic or client_secret_post), so introspection() accepts any request carrying a public client_id with an arbitrary or empty secret. An attacker who holds any access token and knows a public client_id can confirm the token is active and read its metadata, including scope, audience, expiry, and the sub claim. Because sub is computed with the calling RP's user identifier attribute, an attacker can translate user identifiers between Relying Parties, defeating per-client and pseudonymous identifiers. No public proof of concept is known and there is no evidence of exploitation in the wild.
What to do: Upgrade to Lemonldap::NG 2.21.6 (2.20.x/2.21.x branch) or 2.23.4 (2.22.x/2.23.x branch) or later, where the public-RP secret check is enforced. Until patched, restrict network access to the introspection endpoint and review which Relying Parties are configured as public; audit logs for introspection requests presenting public client_ids with empty or invalid secrets. If cross-RP identifier correlation is a concern, assess which user attribute each RP uses for sub and rotate any access tokens issued during the exposure window.
Affected
| Lemonldap::NG::Portal (Perl) | >= 2.20.0, < 2.21.6 |
| Lemonldap::NG::Portal (Perl) | >= 2.22.0, < 2.23.4 |
Estimated exposure
nichelikely hundreds to a few thousand self-hosted identity-provider deployments — Lemonldap::NG is a self-hosted open-source SSO/IAM product with no public install telemetry and a deployment base concentrated in (mainly French) public-sector and enterprise organizations, so exact counts are unknown but well below…
Description
Lemonldap::NG::Portal versions from 2.20.0 before 2.21.6, from 2.22.0 before 2.23.4 for Perl allow unauthenticated OAuth2 token introspection because checkEndPointAuthenticationCredentials does not verify the client secret of a public Relying Party. checkEndPointAuthenticationCredentials() skips the secret comparison for a Relying Party marked public and still returns the authentication method deduced from the request, client_secret_basic or client_secret_post. introspection() rejects a caller only when that method is missing or none, so a request carrying a public client_id and an arbitrary or empty secret passes the endpoint's authentication check. An attacker who holds an access token and knows the client_id of any public Relying Party can confirm the token is active and read its metadata, including scope, audience, expiry and the sub claim. The sub claim is computed with the calling Relying Party's user identifier attribute, so an attacker can translate a user identifier from one Relying Party to another, defeating per-client and pseudonymous identifiers.