Fedora Account System
Red Hat Associate
Red Hat Customer
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.