Identity Crisis: Bypassing OIDC Checks in Sigstore Python (CVE-2026-24408)
Jan 26, 2026·6 min read·6 visits
Executive Summary (TL;DR)
The `sigstore-python` client failed to verify the `state` parameter during OIDC authentication callbacks. This allowed attackers to perform a Localhost CSRF attack, feeding their own authorization codes to a victim's signing process. As a result, a victim could unknowingly sign code with an attacker's identity, creating falsified provenance records.
A critical flaw in the Sigstore Python client's OpenID Connect implementation allows attackers to force victims to sign software artifacts using the attacker's identity. This 'Login CSRF' vulnerability undermines the core promise of software supply chain provenance by allowing identity misbinding.
The Hook: When Trust is a One-Way Street
In the modern software supply chain, we have moved past simply asking 'is this binary not malware?' to asking 'who exactly built this binary?'. Sigstore has become the gold standard for answering that question, acting as a certificate authority that binds OpenID Connect (OIDC) identities (like your email) to cryptographic keys. It’s the digital notary of the open-source world.
But a notary is only as good as their ID verification process. CVE-2026-24408 is a vulnerability in sigstore-python—the official Python client for interacting with Sigstore—that effectively allows someone to swap the ID card just before the notary stamps the document.
This isn't a buffer overflow or a remote code execution exploit in the traditional sense. It's a logic flaw in the authentication handshake. Specifically, it's a "Login CSRF" (Cross-Site Request Forgery). By exploiting this, an attacker doesn't steal your keys; they force their keys into your hands. You do the work, you run the build, but the resulting signature claims it was done by them. It turns the concept of non-repudiation on its head.
The Flaw: OIDC State of Confusion
To understand this bug, you need to understand the OAuth 2.0 and OIDC dance. When you run sigstore sign, the tool spins up a temporary local web server (a callback listener) and opens your browser to an Identity Provider (IdP) like Google or GitHub. To prevent attacks where a malicious site forces you to log in, the protocol uses a state parameter—a random, unguessable string generated by the client.
Here is how it is supposed to work:
- Client generates
state=XYZ123. - Client sends user to IdP with
?state=XYZ123. - IdP authenticates user and redirects back to client with
?code=AUTH_CODE&state=XYZ123. - Client verifies that the returned
statematchesXYZ123.
If the state matches, the client knows the response corresponds to the request it just made. If it doesn't, someone is trying to interfere.
The developers of sigstore-python did steps 1, 2, and 3 perfectly. They generated a cryptographically strong state and sent it along. However, they completely forgot step 4. When the callback hit the local listener, the code simply grabbed the authorization code and proceeded to exchange it for a token. It treated the state parameter as decorative garnish rather than a security control.
This omission meant the client had no way to distinguish between a legitimate response from the user's browser and a forced request injected by a malicious script running in a background tab.
The Code: The Missing Link
The vulnerability resided in sigstore/oidc.py, specifically within the _OAuthSession handling. The code was parsing the callback parameters but lacked the crucial assertion logic.
Here is a reconstruction of the logic flow before the fix. Notice how the state is present in the response dictionary but never compared against the session's stored state:
# VULNERABLE CODE (Simplified)
class OAuthRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
# ... parses query params into auth_response ...
# The code blindly trusts the response
# self.server.auth_response populated from URL params
code = self.server.auth_response["code"][0]
# Proceed to exchange code for token...The fix, introduced in commit 5e77497fe8f0b202bdd118949074ec2f20da69aa, adds the necessary guardrail. It's a textbook example of defensive coding for OIDC:
# PATCHED CODE
# Check if the state returned by the IdP matches what we sent
if self.server.auth_response["state"][0] != self.server.oauth_session.state:
raise IdentityError("OAuth state mismatch")
code = self.server.auth_response["code"][0]By adding these two lines, the client now rejects any unsolicited authentication attempts. If the state doesn't match the one stored in oauth_session, the tool throws an IdentityError and aborts the signing process immediately.
The Exploit: Hijacking the Signing Ceremony
Exploiting this requires a bit of timing, but it is highly reproducible. The attack vector targets the ephemeral local web server that sigstore spins up to receive the OIDC callback.
The Setup:
- Attacker Preparation: The attacker starts an OIDC flow on their own machine but pauses right before the final redirect. They capture a valid authorization code (
ATTACKER_CODE) tied to their own malicious account (e.g.,attacker@evil.com). - The Trap: The attacker hosts a malicious website containing JavaScript designed to hit
http://localhost:[PORT]. Sincesigstoreoften tries a predictable range of ports or the attacker can use aggressive port scanning via JS, finding the listener is feasible.
The Execution:
- The victim runs
sigstore sign my-app.exe. sigstoreopens the victim's browser to authenticate.- While the victim is authenticating (or if they have other tabs open), they visit the attacker's site.
- The attacker's JavaScript fires a request to
http://127.0.0.1:[PORT]/?code=ATTACKER_CODE&state=IRRELEVANT.
The Result:
Because the vulnerable client ignores the state mismatch:
- The victim's
sigstoreclient acceptsATTACKER_CODE. - It exchanges this code for an ID token belonging to
attacker@evil.com. - The client generates a signing certificate for the attacker's identity.
- The victim's software (
my-app.exe) is signed and uploaded to the Rekor transparency log, but the log entry says it was signed byattacker@evil.com.
This is a subtle sabotage. If the victim has automated policy checks that enforce "Binaries must be signed by victim@corp.com", the deployment will fail. Alternatively, this could be used for reputation attacks, making it look like the attacker is the author of the victim's software.
The Impact: Why This Matters
This vulnerability breaks the chain of custody. Sigstore is designed to provide non-falsifiable proof of authorship. By enabling identity misbinding, we introduce a mechanism to corrupt that proof.
While this doesn't allow an attacker to steal the victim's private key or sign malicious code as the victim (which would be a much higher severity issue), it causes Identity Confusion.
Consider an automated build pipeline where a developer signs an artifact before pushing to production. If an attacker can race the OIDC callback and inject their own identity, the artifact enters the pipeline with the wrong metadata. This leads to Denial of Service (if the pipeline rejects the signature) or, more insidiously, a false audit trail. If an attacker injects their identity into a malicious artifact on their own machine, that's normal. But forcing a trusted machine to sign trusted code with an untrusted identity creates chaos in provenance-based policy enforcement systems.
Mitigation: Closing the Loop
The remediation is straightforward: update the client. The logic flaw is entirely contained within the client-side library, so no server-side changes are required from the Sigstore infrastructure.
Immediate Action: Execute the following command to pull the patched version:
pip install --upgrade sigstoreVerification: Ensure you are running version 4.2.0 or higher:
pip show sigstoreFor developers integrating sigstore-python as a library, ensure your requirements.txt or pyproject.toml pins the version to >=4.2.0. If you have built custom tooling around the OIDC flow, review your own code to ensure you are strictly validating the state parameter in any OAuth 2.0 implementation. This vulnerability serves as a potent reminder: cryptographic protocols are fragile; skipping even a single validation step can render the entire handshake insecure.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:L/A:LAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
sigstore-python sigstore | < 4.2.0 | 4.2.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-352 (CSRF) |
| CVSS v3.1 | 6.1 (Medium) |
| Attack Vector | Network (Localhost CSRF) |
| Impact | Identity Misbinding |
| EPSS Score | 0.00039 |
| Exploit Status | PoC Available |
MITRE ATT&CK Mapping
The application does not verify the integrity of the OIDC state parameter, allowing unauthorized authentication responses to be processed.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.