Skip to content

Unauthenticated SSRF via issuer allowlist prefix bypass in OIDC verification

High
lukpueh published GHSA-j3g8-hf9c-x4ww Jul 2, 2026

Software

eclipse-csi/pia

Affected versions

<= 0.3.0

Patched versions

None

Description

Summary

PIA's OIDC issuer allowlist for Jenkins tokens uses a bare string-prefix check (issuer.startswith('https://ci.eclipse.org') in is_issuer_known, pia/models.py:139) instead of validating the issuer as a properly host-bounded URL. An attacker can craft an issuer such as https://ci.eclipse.org@evil.host (userinfo trick) or https://ci.eclipse.org.evil.host (suffix trick) that satisfies the prefix check while pointing the OIDC discovery and JWKS fetches at a server the attacker controls. An unauthenticated caller of POST /v1/upload/sbom can use this to force PIA to make outbound HTTP(S) requests to an arbitrary attacker-chosen host, and to have oidc.verify_token accept a JWT signed with the attacker's own key.

Impact

An unauthenticated remote attacker can trigger blind server-side requests (SSRF) from PIA to any host of their choosing — including cloud metadata endpoints and internal-only services — by supplying a crafted iss claim in an unverified bearer token. PIA's fetches to {issuer}/.well-known/openid-configuration and the resulting jwks_uri (pia/oidc.py:27,31,51-52) happen before any signature check or workload lookup, so no registered workload or valid credential is required to trigger the outbound request. Separately, because the fetched JWKS is entirely attacker-supplied, verify_token will accept a token signed with the attacker's own key and return attacker-chosen claims, subverting the intended OIDC signature trust root.

This does not by itself complete an authentication bypass: find_workload_by_claims (pia/models.py:170) still requires the verified issuer to exactly equal a Workload.issuer value already registered in PIA's database, so an attacker cannot simultaneously satisfy that exact match while routing the fetch to their own server. The confirmed, disclosable impact is unauthenticated blind SSRF plus erosion of the OIDC verification trust boundary — not a standalone end-to-end auth bypass.

Affected versions

  • <= 0.3.0 (all released versions through the current HEAD c8f57af; Jenkins OIDC support — and this prefix-match check — has been present since the 0.1.0 initial release)

Patched versions

Not yet patched. No fix_commit is set and main at HEAD c8f57af still contains the vulnerable prefix check in is_issuer_known.

Proof of concept

Reproduced in-container against the vulnerable functions directly:

from pia.models import is_issuer_known

for iss in [
    "https://ci.eclipse.org@127.0.0.1:8888",     # userinfo trick -> real host is 127.0.0.1
    "https://ci.eclipse.org.attacker.example",   # suffix trick
]:
    print(iss, "->", is_issuer_known(iss))

Output:

https://ci.eclipse.org@127.0.0.1:8888 -> True
https://ci.eclipse.org.attacker.example -> True

Calling oidc.verify_token(token, "https://ci.eclipse.org@127.0.0.1:8888", audience) causes a local TCP listener bound to 127.0.0.1:8888 to receive PIA's outbound connection — confirming the fetch target is attacker-controlled, not ci.eclipse.org. A second PoC serves a self-signed-CA-trusted HTTPS discovery document and JWKS at that address; verify_token then accepts a JWT signed with the attacker's own RSA key and returns the attacker-chosen claims, confirming the OIDC trust root is subverted.

Fix suggestion

Validate the issuer as a parsed URL with an exact host match (e.g. urlparse(issuer).hostname == "ci.eclipse.org", or a suffix check anchored on a dotted domain boundary) in is_issuer_known (pia/models.py:139), rather than a bare str.startswith prefix comparison.

References

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N

CVE ID

CVE-2026-14336

Weaknesses

Server-Side Request Forgery (SSRF)

The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination. Learn more on MITRE.