From 80fd7f119b628878b91b16f008b6178963223ad0 Mon Sep 17 00:00:00 2001 From: faro Date: Fri, 3 Jul 2026 22:50:59 +0300 Subject: [PATCH 1/2] feat(lab8): md report --- submissions/lab8.md | 359 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 submissions/lab8.md diff --git a/submissions/lab8.md b/submissions/lab8.md new file mode 100644 index 00000000..26e609d0 --- /dev/null +++ b/submissions/lab8.md @@ -0,0 +1,359 @@ +# Lab 8 — Submission + +## Task 1: Sign + Tamper Demo + +### Registry and image push + +- Registry container name: `lab8-registry`, listening on `localhost:5000` +- Base registry image: `registry:3` +- Pushed image: `localhost:5000/juice-shop:v20.0.0` +- Digest of the pushed image: + +```text +localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe +``` +The digest was obtained from the local registry API after the image was pushed. Because Docker sent a single-platform manifest to the local registry, the digest differs from the multi-platform manifest on Docker Hub. + +## Signing + +The image was signed by its digest using the generated Cosign private key: + +```text +Signing: localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe +Pushing signature to: localhost:5000/juice-shop +The private key lives at labs/lab8/keys/cosign.key and is excluded via the repository's *.key gitignore rule. Only the public key at labs/lab8/keys/cosign.pub is committed. +``` + +## Verification (PASSED) + +Command: + +```bash +cosign verify \ + --key labs/lab8/keys/cosign.pub \ + --allow-insecure-registry \ + --insecure-ignore-tlog \ + "localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe" +``` +Output: + +```text +WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the signature. + +Verification for localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe -- +The following checks were performed on each of these signatures: + - The cosign claims were validated + - The signatures were verified against the specified public key +``` +Verified claims: + +```json +[ + { + "critical": { + "identity": { + "docker-reference": "localhost:5000/juice-shop" + }, + "image": { + "docker-manifest-digest": "sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe" + }, + "type": "cosign container image signature" + }, + "optional": null + } +] +``` +Exit code: + +```text +0 +``` + +## Tamper demo (FAILED – correct behaviour) + +A different image (alpine:3.20) was pushed under a tag that looks similar: + +```text +localhost:5000/juice-shop:v20.0.0-tampered +``` +Original digest: + +```text +localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe +``` +Tampered (Alpine) digest: + +```text +localhost:5000/juice-shop@sha256:c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e +``` +Result of cosign verify on the tampered digest: + +```text +WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the signature. +Error: no signatures found +error during command execution: no signatures found +``` +Exit code: + +```text +10 +``` +The failure is expected because no Cosign signature exists for the Alpine manifest digest. + +## Sanity check – original signature still valid + +After pushing the tampered image, the original digest was verified again: + +```text +Verification for localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe -- +The following checks were performed on each of these signatures: + - The cosign claims were validated + - The signatures were verified against the specified public key + +[{"critical":{"identity":{"docker-reference":"localhost:5000/juice-shop"},"image":{"docker-manifest-digest":"sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe"},"type":"cosign container image signature"},"optional":null}] +``` +Exit code: + +```text +0 +``` +### Importance of digest binding + +An OCI tag is merely a mutable label that can be reassigned to a different manifest while the repository name stays the same. Cosign signs the fixed manifest digest, so replacing the image or retagging changes the digest and breaks signature verification. + +If Cosign signed only the tag name, an attacker could redirect the signed-looking tag to a malicious image, preserving the human-readable reference. Digest binding prevents such substitution because the signature is valid only for the exact bytes represented by the original digest. + +## Task 2: SBOM + Provenance Attestations + +### SBOM attestation +``` +Attached: yes +Attestation type: cyclonedx +Command exit code: 0 +Source SBOM file: labs/lab4/juice-shop.cdx.json +Source SBOM format: CycloneDX 1.6 +``` +Command: + +```bash +cosign attest \ + --key labs/lab8/keys/cosign.key \ + --type cyclonedx \ + --predicate labs/lab4/juice-shop.cdx.json \ + --allow-insecure-registry \ + --tlog-upload=false \ + --yes \ + "$DIGEST" +``` +Output: + +```text +Using payload from: labs/lab4/juice-shop.cdx.json +``` +Decoded payload excerpt from verify-attestation: +``` +json +{ + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://cyclonedx.org/bom", + "subject": [ + { + "name": "localhost:5000/juice-shop", + "digest": { + "sha256": "28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe" + } + } + ], + "predicate": { + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "components": [ + { + "author": "Benjamin Byholm (https://github.com/kkoopa/), Mathias Küsel (https://github.com/mathiask88/)", + "bom-ref": "pkg:npm/1to2@1.0.0?package-id=3cea2309a653e6ed", + "cpe": "cpe:2.3:a:nodejs:1to2:1.0.0:*:*:*:*:*:*:*", + "description": "NAN 1 -> 2 Migration Script", + "externalReferences": [ + { + "type": "distribution", + "url": "git://github.com/nodejs/nan.git" + } + ], + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ] + } + ] + } +} +``` +Component counts: + +```text +Source: 1846 +Extracted: 1846 +Component count matches Lab 4 source: yes +``` + +Diff command: + +```bash +diff \ + <(jq -S '.components | length' labs/lab4/juice-shop.cdx.json) \ + <(jq -S '.components | length' labs/lab8/results/sbom-from-attestation.json) +``` +Diff output was empty, exit code 0, confirming identical component counts. + +## Provenance attestation +``` +Attached: yes +Attestation type: slsaprovenance +Command exit code: 0 +Builder ID: https://localhost/lab8-student +Build type: https://example.com/lab8/local-build +Source repository: https://github.com/m1d0rfeed/DevSecOps-Intro +Source commit: f9932471eb75787a0588c96ee2fc4f65ed0772e8 +``` +Predicate body: +``` +json +{ + "builder": { + "id": "https://localhost/lab8-student" + }, + "buildType": "https://example.com/lab8/local-build", + "invocation": { + "configSource": { + "uri": "https://github.com/m1d0rfeed/DevSecOps-Intro", + "digest": { + "sha1": "f9932471eb75787a0588c96ee2fc4f65ed0772e8" + } + } + } +} +``` +Verification output: + +```text +Verification for localhost:5000/juice-shop@sha256:28870b9d2bec49e605d6ebbf4b22ed1ec1ca0a72347ef19217bbbb21ea44e3fe -- +The following checks were performed on each of these signatures: + - The cosign claims were validated + - The signatures were verified against the specified public key +``` +Decoded provenance predicate: +``` +json +{ + "builder": { + "id": "https://localhost/lab8-student" + }, + "buildType": "https://example.com/lab8/local-build", + "invocation": { + "configSource": { + "uri": "https://github.com/m1d0rfeed/DevSecOps-Intro", + "digest": { + "sha1": "f9932471eb75787a0588c96ee2fc4f65ed0772e8" + } + } + } +} +``` +### Value for a future verifier (Lab 9) + +A valid image signature confirms that the image hasn’t been altered since signing, but it doesn’t describe what software is inside. An attached, verified SBOM provides a signed inventory that an admission controller or security workflow can check against vulnerability databases and policy rules. + +When a new vulnerability like Log4Shell is disclosed, an unsigned image or one without an SBOM would still need rescanning or manual investigation to decide whether the affected library is present. With a signed and verified SBOM, the vulnerable component and version can be matched immediately against the attested inventory. A Kyverno verifyImages policy can then require both the signature and the expected attestation predicate before allowing admission. + +## Bonus: Blob Signing (Codecov 2021 mitigation) + +### Release artifact + +A sample installer was packaged into a tarball: + +```bash +cat > /tmp/install.sh <<'EOF' +#!/bin/bash +echo "Welcome to my-cool-tool installer" +echo "Running setup..." +EOF + +chmod +x /tmp/install.sh +tar -czf labs/lab8/results/my-tool.tar.gz -C /tmp install.sh +``` +Archive contents: + +```text +install.sh +``` +### Sign and verify + +Signed files: + +```text +my-tool.tar.gz +my-tool.tar.gz.bundle +``` +Signing command: + +```bash +cosign sign-blob \ + --key labs/lab8/keys/cosign.key \ + --yes \ + --tlog-upload=false \ + --bundle labs/lab8/results/my-tool.tar.gz.bundle \ + labs/lab8/results/my-tool.tar.gz +``` +Signing output: + +```text +Using payload from: labs/lab8/results/my-tool.tar.gz +Wrote bundle to file labs/lab8/results/my-tool.tar.gz.bundle +``` +Verification command: + +```bash +cosign verify-blob \ + --key cosign.pub \ + --bundle my-tool.tar.gz.bundle \ + --insecure-ignore-tlog \ + my-tool.tar.gz +``` +Successful verification output: + +```text +WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the blob. +Verified OK +``` +Exit code: + +```text +0 +``` +### Tamper test failed (as expected) + +The archive was modified after signing: + +```bash +echo "MALICIOUS PAYLOAD" >> my-tool.tar.gz +``` +Verification result: + +```text +WARNING: Skipping tlog verification is an insecure practice that lacks of transparency and auditability verification for the blob. +Error: invalid signature when validating ASN.1 encoded signature +error during command execution: invalid signature when validating ASN.1 encoded signature +``` +Exit code: + +```text +1 +``` +## Mitigation of the Codecov 2021 incident + +The Codecov Bash uploader was consumed by CI pipelines without any cryptographic check, so a modified uploader could execute while still being fetched from a trusted location. If users had obtained the uploader together with a trusted Cosign public key and a signature bundle, they could have run cosign verify-blob before executing the script. + +A tampered uploader would no longer match the signed byte stream, generating an invalid-signature error like the one above. The CI job could then abort before invoking bash, preventing the malicious script from accessing environment variables, credentials, and other secrets available to the build process. From 023a2b5835a02232efc237012780baaef5122083 Mon Sep 17 00:00:00 2001 From: faro Date: Fri, 3 Jul 2026 22:53:10 +0300 Subject: [PATCH 2/2] feat(lab8): key --- labs/lab8/keys/cosign.pub | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 labs/lab8/keys/cosign.pub diff --git a/labs/lab8/keys/cosign.pub b/labs/lab8/keys/cosign.pub new file mode 100644 index 00000000..73bbbe11 --- /dev/null +++ b/labs/lab8/keys/cosign.pub @@ -0,0 +1,2 @@ +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5ktDSD78DFA0jzMFZGXYl+KWS4WX +bB8f69nGZiO7GcWpyeQjox1YpFTm7lc8hT3W72FT/ff8fls/ZVlzBtn88w==