Cosign verification accepts any valid Rekor entry under certain conditions
Description
Cosign provides code signing and transparency for containers and binaries. Prior to versions 2.6.2 and 3.0.4, Cosign bundle can be crafted to successfully verify an artifact even if the embedded Rekor entry does not reference the artifact's digest, signature or public key. When verifying a Rekor entry, Cosign verifies the Rekor entry signature, and also compares the artifact's digest, the user's public key from either a Fulcio certificate or provided by the user, and the artifact signature to the Rekor entry contents. Without these comparisons, Cosign would accept any response from Rekor as valid. A malicious actor that has compromised a user's identity or signing key could construct a valid Cosign bundle by including any arbitrary Rekor entry, thus preventing the user from being able to audit the signing event. This issue has been patched in versions 2.6.2 and 3.0.4.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/sigstore/cosign/v3Go | < 3.0.4 | 3.0.4 |
github.com/sigstore/cosign/v2Go | < 2.6.2 | 2.6.2 |
Affected products
1Patches
16832fba4928cFix bundle verify path for old bundle/trusted root (#4623)
2 files changed · +109 −29
pkg/cosign/verify.go+31 −29 modified@@ -1213,6 +1213,36 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { return false, errors.New("no trusted rekor public keys provided") } + if err := compareSigs(bundle.Payload.Body.(string), sig); err != nil { + return false, err + } + + if err := comparePublicKey(bundle.Payload.Body.(string), sig, co); err != nil { + return false, err + } + + payload, err := sig.Payload() + if err != nil { + return false, fmt.Errorf("reading payload: %w", err) + } + signature, err := sig.Base64Signature() + if err != nil { + return false, fmt.Errorf("reading base64signature: %w", err) + } + + alg, bundlehash, err := bundleHash(bundle.Payload.Body.(string), signature) + if err != nil { + return false, fmt.Errorf("computing bundle hash: %w", err) + } + h := sha256.Sum256(payload) + payloadHash := hex.EncodeToString(h[:]) + + if alg != "sha256" { + return false, fmt.Errorf("unexpected algorithm: %q", alg) + } else if bundlehash != payloadHash { + return false, fmt.Errorf("matching bundle to payload: bundle=%q, payload=%q", bundlehash, payloadHash) + } + if co.TrustedMaterial != nil { payload := bundle.Payload logID, err := hex.DecodeString(payload.LogID) @@ -1227,6 +1257,7 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { if err := tlog.VerifySET(entry, co.TrustedMaterial.RekorLogs()); err != nil { return false, fmt.Errorf("verifying bundle with trusted root: %w", err) } + return true, nil } // Make sure all the rekorPubKeys are ecsda.PublicKeys @@ -1236,14 +1267,6 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { } } - if err := compareSigs(bundle.Payload.Body.(string), sig); err != nil { - return false, err - } - - if err := comparePublicKey(bundle.Payload.Body.(string), sig, co); err != nil { - return false, err - } - pubKey, ok := co.RekorPubKeys.Keys[bundle.Payload.LogID] if !ok { return false, &VerificationFailure{ @@ -1258,27 +1281,6 @@ func VerifyBundle(sig oci.Signature, co *CheckOpts) (bool, error) { fmt.Fprintf(os.Stderr, "**Info** Successfully verified Rekor entry using an expired verification key\n") } - payload, err := sig.Payload() - if err != nil { - return false, fmt.Errorf("reading payload: %w", err) - } - signature, err := sig.Base64Signature() - if err != nil { - return false, fmt.Errorf("reading base64signature: %w", err) - } - - alg, bundlehash, err := bundleHash(bundle.Payload.Body.(string), signature) - if err != nil { - return false, fmt.Errorf("computing bundle hash: %w", err) - } - h := sha256.Sum256(payload) - payloadHash := hex.EncodeToString(h[:]) - - if alg != "sha256" { - return false, fmt.Errorf("unexpected algorithm: %q", alg) - } else if bundlehash != payloadHash { - return false, fmt.Errorf("matching bundle to payload: bundle=%q, payload=%q", bundlehash, payloadHash) - } return true, nil }
pkg/cosign/verify_test.go+78 −0 modified@@ -782,6 +782,84 @@ func TestVerifyImageSignatureWithSigVerifierAndRekorTSA(t *testing.T) { } } +func TestVerifyImageSignatureWithMismatchedBundleAndTrustedRoot(t *testing.T) { + ctx := context.Background() + var ca root.FulcioCertificateAuthority + rootCert, rootKey, _ := test.GenerateRootCa() + ca.Root = rootCert + sv, _, err := signature.NewECDSASignerVerifier(elliptic.P256(), rand.Reader, crypto.SHA256) + if err != nil { + t.Fatalf("creating signer: %v", err) + } + + leafCert, privKey, _ := test.GenerateLeafCert("subject@mail.com", "oidc-issuer", rootCert, rootKey) + pemLeaf := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert.Raw}) + + rootPool := x509.NewCertPool() + rootPool.AddCert(rootCert) + + payload := []byte{1, 2, 3, 4} + h := sha256.Sum256(payload) + signature1, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) + + // Create a fake bundle + pe, _ := proposedEntries(base64.StdEncoding.EncodeToString(signature1), payload, pemLeaf) + entry, _ := rtypes.UnmarshalEntry(pe[0]) + leaf, _ := entry.Canonicalize(ctx) + rekorBundle := CreateTestBundle(ctx, t, sv, leaf) + pemBytes, _ := cryptoutils.MarshalPublicKeyToPEM(sv.Public()) + rekorPubKeys := NewTrustedTransparencyLogPubKeys() + rekorPubKeys.AddTransparencyLogPubKey(pemBytes, tuf.Active) + + tlogs := make(map[string]*root.TransparencyLog) + for k, v := range rekorPubKeys.Keys { + tlogs[k] = &root.TransparencyLog{PublicKey: v.PubKey, HashFunc: crypto.SHA256, ValidityPeriodStart: time.Now().Add(-1 * time.Minute)} + } + + trustedRoot, err := root.NewTrustedRoot(root.TrustedRootMediaType01, []root.CertificateAuthority{&ca}, nil, nil, tlogs) + if err != nil { + t.Fatal(err) + } + + // Create a different bundle for a different signature + signature2, _ := privKey.Sign(rand.Reader, h[:], crypto.SHA256) + pe2, _ := proposedEntries(base64.StdEncoding.EncodeToString(signature2), payload, pemLeaf) + entry2, _ := rtypes.UnmarshalEntry(pe2[0]) + leaf2, _ := entry2.Canonicalize(ctx) + rekorBundle2 := CreateTestBundle(ctx, t, sv, leaf2) + + opts := []static.Option{static.WithCertChain(pemLeaf, []byte{}), static.WithBundle(rekorBundle2)} + // Create a signed entity for the original signature but with the wrong bundle for that signature + ociSig, _ := static.NewSignature(payload, base64.StdEncoding.EncodeToString(signature1), opts...) + + _, err = VerifyImageSignature(context.TODO(), ociSig, v1.Hash{}, + &CheckOpts{ + RootCerts: rootPool, + IgnoreSCT: true, + Identities: []Identity{{Subject: "subject@mail.com", Issuer: "oidc-issuer"}}, + TrustedMaterial: trustedRoot}) + if err == nil || !strings.Contains(err.Error(), "signature in bundle does not match signature being verified") { + t.Fatalf("expected error for mismatched signature and bundle, got %v", err) + } + + // Create a signed entity with a different key from the bundle + leafCert2, _, _ := test.GenerateLeafCert("subject@mail.com", "oidc-issuer", rootCert, rootKey) + pemLeaf2 := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leafCert2.Raw}) + + opts = []static.Option{static.WithCertChain(pemLeaf2, []byte{}), static.WithBundle(rekorBundle)} + ociSig, _ = static.NewSignature(payload, base64.StdEncoding.EncodeToString(signature1), opts...) + + _, err = VerifyImageSignature(context.TODO(), ociSig, v1.Hash{}, + &CheckOpts{ + RootCerts: rootPool, + IgnoreSCT: true, + Identities: []Identity{{Subject: "subject@mail.com", Issuer: "oidc-issuer"}}, + TrustedMaterial: trustedRoot}) + if err == nil || !strings.Contains(err.Error(), "error verifying bundle: comparing public key PEMs") { + t.Fatal(err) + } +} + func TestValidateAndUnpackCertSuccess(t *testing.T) { subject := "email@email" oidcIssuer := "https://accounts.google.com"
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-whqx-f9j3-ch6mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-22703ghsaADVISORY
- github.com/sigstore/cosign/commit/6832fba4928c1ad69400235bbc41212de5006176ghsax_refsource_MISCWEB
- github.com/sigstore/cosign/pull/4623ghsax_refsource_MISCWEB
- github.com/sigstore/cosign/security/advisories/GHSA-whqx-f9j3-ch6mghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.