Bug 2568072 (CVE-2026-123710) - CVE-2026-123710 quay: Red Hat Quay: unauthenticated registry signing-key takeover via service key rotation
Summary: CVE-2026-123710 quay: Red Hat Quay: unauthenticated registry signing-key take...
Keywords:
Status: NEW
Alias: CVE-2026-123710
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
urgent
urgent
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-11-06 10:22 UTC by OSIDB Bzimport
Modified: 2026-11-11 15:17 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-11-06 10:22:33 UTC
An unauthenticated client can install an attacker-controlled, never-expiring service signing key in Red Hat Quay and then forge registry tokens that grant read and write access to every repository.

The service key server authorises key rotation by checking only that the supplied bearer JWT is RS256-signed by an already-approved key for that service and carries the expected audience and issuer. It does not check what the token was issued for. /v2/auth mints exactly such a token for anonymous callers when FEATURE_ANONYMOUS_ACCESS is enabled, and the audience is taken from the client-supplied service parameter with no pinning. The key server ignores the token's grants.

The attacker PUTs their own JWK to /keys/services/quay/keys/<kid>. replace_service_key copies the signer's approval onto the new key and stores it with no expiration when the expiration parameter is omitted. The new key is trusted immediately, and registry JWTs signed with it are honoured on /v2 for any repository, including push and overwrite. Rotation also deletes the legitimate instance key as a side effect, and the attacker key can delete replacement instance keys.

Root cause references:
- endpoints/v2/v2auth.py: audience_param = request.args.get("service")
- util/security/registry_jwt.py: tokens include access and context claims
- endpoints/keyserver/__init__.py: _validate_jwt checks signature, aud and iss only, then discards the payload
- data/model/service_keys.py: replace_service_key copies approval, accepts a null expiration, and deletes the signer
- auth/registry_jwt_auth.py: access claims map directly to pull, push and * grants

Confirmed against master 9226d3a5 and the 3.19-unstable image. The logic is present from the first public commit.


Note You need to log in before you can comment on or make changes to this bug.