CVE-2022-35929 (published 2022-08-04T19:15Z)

cosign is a container signing and verification utility. In versions prior to 1.10.1 cosign can report a false positive if any attestation exists. `cosign verify-attestation` used with the `--type` flag will report a false positive verification when there is at least one attestation with a valid signature and there are NO attestations of the type being verified (--type defaults to "custom"). This can happen when signing with a standard keypair and with "keyless" signing with Fulcio. This vulnerability can be reproduced with the `distroless.dev/static@sha256:dd7614b5a12bc4d617b223c588b4e0c833402b8f4991fb5702ea83afad1986e2` image. This image has a `vuln` attestation but not an `spdx` attestation. However, if you run `cosign verify-attestation --type=spdx` on this image, it incorrectly succeeds. This issue has been addressed in version 1.10.1 of cosign. Users are advised to upgrade. There are no known workarounds for this issue.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-347
cwe_name: Improper Verification of Cryptographic Signature
description: The product does not verify, or incorrectly verifies, the cryptographic signature for data.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/sigstore/cosign
commit_details: repo_url: https://github.com/sigstore/cosign
author: Matt Moore
author_date: 2022-08-04 09:05:27-07:00
msg: Merge pull request from GHSA-vjxv-45g9-9296

Today the verification logic:
1. Verifies signatures on attestations (at least one must verify, or it errors),
2. All attestations matching the specified `--type` must pass any specified Cue/Rego policies,
3. *All* signature-verified attestations are then printed.

However, if NONE of the attestations match the specified `--type` then `2.` is considered satisfied and we proceed to `3.`

This changes the above logic to:
1. Same.
2. Same, but these are put into a `checked` list,
3. `checked` must be non-empty (or an error is printed about no attestations matching `--type`),
4. *Just* the `checked` attestations are printed.

---

The bug at HEAD:
```shell
$ cosign verify-attestation --type spdx ghcr.io/distroless/static@sha256:dd7614b5a12bc4d617b223c588b4e0c833402b8f4991fb5702ea83afad1986e2

Verification for ghcr.io/distroless/static@sha256:dd7614b5a12bc4d617b223c588b4e0c833402b8f4991fb5702ea83afad1986e2 --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - Any certificates were verified against the Fulcio roots.
Certificate subject:  https://github.com/distroless/static/.github/workflows/release.yaml@refs/heads/main
Certificate issuer URL:  https://token.actions.githubusercontent.com
Certificate extension GitHub Workflow Trigger: schedule
Certificate extension GitHub Workflow SHA: 7e7572e578de7c51a2f1a1791f025cf315503aa2
Certificate extension GitHub Workflow Name: Create Release
Certificate extension GitHub Workflow Trigger distroless/static
Certificate extension GitHub Workflow Ref: refs/heads/main
{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjb3NpZ24uc2lnc3RvcmUuZGV2L2F0dGVzdGF0aW9uL3Z1bG4vdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2hjci5pby9kaXN0cm9sZXNzL3N0YXRpYyIsImRpZ2VzdCI6eyJzaGEyNTYiOiJkZDc2MTRiNWExMmJjNGQ2MTdiMjIzYzU4OGI0ZTBjODMzNDAyYjhmNDk5MWZiNTcwMmVhODNhZmFkMTk4NmUyIn19XSwicHJlZGljYXRlIjp7Imludm9jYXRpb24iOnsicGFyYW1ldGVycyI6bnVsbCwidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2Rpc3Ryb2xlc3Mvc3RhdGljL2FjdGlvbnMvcnVucy8yNzc5MjEyNzA1IiwiZXZlbnRfaWQiOiIyNzc5MjEyNzA1IiwiYnVpbGRlci5pZCI6IkNyZWF0ZSBSZWxlYXNlIn0sInNjYW5uZXIiOnsidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2FxdWFzZWN1cml0eS90cml2eSIsInZlcnNpb24iOiIwLjI5LjIiLCJkYiI6eyJ1cmkiOiIiLCJ2ZXJzaW9uIjoiIn0sInJlc3VsdCI6eyIkc2NoZW1hIjoiaHR0cHM6Ly9qc29uLnNjaGVtYXN0b3JlLm9yZy9zYXJpZi0yLjEuMC1ydG0uNS5qc29uIiwicnVucyI6W3siY29sdW1uS2luZCI6InV0ZjE2Q29kZVVuaXRzIiwib3JpZ2luYWxVcmlCYXNlSWRzIjp7IlJPT1RQQVRIIjp7InVyaSI6ImZpbGU6Ly8vIn19LCJyZXN1bHRzIjpbXSwidG9vbCI6eyJkcml2ZXIiOnsiZnVsbE5hbWUiOiJUcml2eSBWdWxuZXJhYmlsaXR5IFNjYW5uZXIiLCJpbmZvcm1hdGlvblVyaSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hcXVhc2VjdXJpdHkvdHJpdnkiLCJuYW1lIjoiVHJpdnkiLCJydWxlcyI6W10sInZlcnNpb24iOiIwLjI5LjIifX19XSwidmVyc2lvbiI6IjIuMS4wIn19LCJtZXRhZGF0YSI6eyJzY2FuU3RhcnRlZE9uIjoiMjAyMi0wOC0wMlQwMjozMzo0N1oiLCJzY2FuRmluaXNoZWRPbiI6IjIwMjItMDgtMDJUMDI6MzM6NTNaIn19fQ==","signatures":[{"keyid":"","sig":"MEYCIQCovBtLOBXyB2zpvhp3j6QzqLtsH0/RC7fRINSApySqxAIhAIKlzu1fXuKPPOIheNnsPmBOB6XfZbRs5sDW1yFSch1A"}]}
```

The same with this change:
```shell
$ go run ./cmd/cosign verify-attestation --type spdx ghcr.io/distroless/static@sha256:dd7614b5a12bc4d617b223c588b4e0c833402b8f4991fb5702ea83afad1986e2
Error: none of the attestations matched the predicate type: spdx
main.go:62: error during command execution: none of the attestations matched the predicate type: spdx
exit status 1
```

A valid `--type` with this change:
```shell
$ go run ./cmd/cosign verify-attestation --type vuln ghcr.io/distroless/static@sha256:dd7614b5a12bc4d617b223c588b4e0c833402b8f4991fb5702ea83afad1986e2

Verification for ghcr.io/distroless/static@sha256:dd7614b5a12bc4d617b223c588b4e0c833402b8f4991fb5702ea83afad1986e2 --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - Any certificates were verified against the Fulcio roots.
Certificate subject:  https://github.com/distroless/static/.github/workflows/release.yaml@refs/heads/main
Certificate issuer URL:  https://token.actions.githubusercontent.com
Certificate extension GitHub Workflow Trigger: schedule
Certificate extension GitHub Workflow SHA: 7e7572e578de7c51a2f1a1791f025cf315503aa2
Certificate extension GitHub Workflow Name: Create Release
Certificate extension GitHub Workflow Trigger distroless/static
Certificate extension GitHub Workflow Ref: refs/heads/main
{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJjb3NpZ24uc2lnc3RvcmUuZGV2L2F0dGVzdGF0aW9uL3Z1bG4vdjEiLCJzdWJqZWN0IjpbeyJuYW1lIjoiZ2hjci5pby9kaXN0cm9sZXNzL3N0YXRpYyIsImRpZ2VzdCI6eyJzaGEyNTYiOiJkZDc2MTRiNWExMmJjNGQ2MTdiMjIzYzU4OGI0ZTBjODMzNDAyYjhmNDk5MWZiNTcwMmVhODNhZmFkMTk4NmUyIn19XSwicHJlZGljYXRlIjp7Imludm9jYXRpb24iOnsicGFyYW1ldGVycyI6bnVsbCwidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2Rpc3Ryb2xlc3Mvc3RhdGljL2FjdGlvbnMvcnVucy8yNzc5MjEyNzA1IiwiZXZlbnRfaWQiOiIyNzc5MjEyNzA1IiwiYnVpbGRlci5pZCI6IkNyZWF0ZSBSZWxlYXNlIn0sInNjYW5uZXIiOnsidXJpIjoiaHR0cHM6Ly9naXRodWIuY29tL2FxdWFzZWN1cml0eS90cml2eSIsInZlcnNpb24iOiIwLjI5LjIiLCJkYiI6eyJ1cmkiOiIiLCJ2ZXJzaW9uIjoiIn0sInJlc3VsdCI6eyIkc2NoZW1hIjoiaHR0cHM6Ly9qc29uLnNjaGVtYXN0b3JlLm9yZy9zYXJpZi0yLjEuMC1ydG0uNS5qc29uIiwicnVucyI6W3siY29sdW1uS2luZCI6InV0ZjE2Q29kZVVuaXRzIiwib3JpZ2luYWxVcmlCYXNlSWRzIjp7IlJPT1RQQVRIIjp7InVyaSI6ImZpbGU6Ly8vIn19LCJyZXN1bHRzIjpbXSwidG9vbCI6eyJkcml2ZXIiOnsiZnVsbE5hbWUiOiJUcml2eSBWdWxuZXJhYmlsaXR5IFNjYW5uZXIiLCJpbmZvcm1hdGlvblVyaSI6Imh0dHBzOi8vZ2l0aHViLmNvbS9hcXVhc2VjdXJpdHkvdHJpdnkiLCJuYW1lIjoiVHJpdnkiLCJydWxlcyI6W10sInZlcnNpb24iOiIwLjI5LjIifX19XSwidmVyc2lvbiI6IjIuMS4wIn19LCJtZXRhZGF0YSI6eyJzY2FuU3RhcnRlZE9uIjoiMjAyMi0wOC0wMlQwMjozMzo0N1oiLCJzY2FuRmluaXNoZWRPbiI6IjIwMjItMDgtMDJUMDI6MzM6NTNaIn19fQ==","signatures":[{"keyid":"","sig":"MEYCIQCovBtLOBXyB2zpvhp3j6QzqLtsH0/RC7fRINSApySqxAIhAIKlzu1fXuKPPOIheNnsPmBOB6XfZbRs5sDW1yFSch1A"}]}
```

Signed-off-by: Matt Moore <mattmoor@chainguard.dev>
repository: repo_url: https://github.com/sigstore/cosign
repo_name: sigstore/cosign
description: Code signing and transparency for containers and binaries
repo_language: Go
owner: sigstore
file_changes: filename: verify_attestation.go
change_type: ModificationType.MODIFY
diff: @@ -201,6 +201,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
 			}
 		}
 
+		var checked []oci.Signature
 		var validationErrors []error
 		for _, vp := range verified {
 			payload, err := policy.AttestationToPayloadJSON(ctx, c.PredicateType, vp)
@@ -217,6 +218,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
 				cueValidationErr := cue.ValidateJSON(payload, cuePolicies)
 				if cueValidationErr != nil {
 					validationErrors = append(validationErrors, cueValidationErr)
+					continue
 				}
 			}
 
@@ -225,8 +227,11 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
 				regoValidationErrs := rego.ValidateJSON(payload, regoPolicies)
 				if len(regoValidationErrs) > 0 {
 					validationErrors = append(validationErrors, regoValidationErrs...)
+					continue
 				}
 			}
+
+			checked = append(checked, vp)
 		}
 
 		if len(validationErrors) > 0 {
@@ -237,10 +242,14 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
 			return fmt.Errorf("%d validation errors occurred", len(validationErrors))
 		}
 
+		if len(checked) == 0 {
+			return fmt.Errorf("none of the attestations matched the predicate type: %s", c.PredicateType)
+		}
+
 		// TODO: add CUE validation report to `PrintVerificationHeader`.
 		PrintVerificationHeader(imageRef, co, bundleVerified, fulcioVerified)
 		// The attestations are always JSON, so use the raw "text" mode for outputting them instead of conversion
-		PrintVerification(imageRef, verified, "text")
+		PrintVerification(imageRef, checked, "text")
 	}
 
 	return nil

programming_language: Go