Skip to content

Signed-mode admission is malleable: rollback + mix-and-match of independently-signed artifacts #2

Description

@HarryR

Summary

In ed25519 signed mode, admission pins a long-term release public key, not a specific artifact hash, so any artifact signed by that key is accepted (this is what enables "re-sign, reboot" roll-forward). An actor who controls the serving endpoint(s)/mirrors -- but does NOT hold the private key -- cannot forge a payload, but CAN mix and match knowledge of previously-signed artifacts. Filing to track; this is a design decision, not a code bug.

What an endpoint controller can do (no forgery)

  1. Rollback / downgrade. Serve an OLD but validly-signed payload (e.g. a previous stage2 / UKI with a since-patched vulnerability). The pinned key still verifies it, and there is no version or monotonic counter, so the downgrade is invisible to admission.
  2. Mix-and-match. The payload and its signed args (args_url / args_sig_url) are signed INDEPENDENTLY with no binding between them. Serve the payload from release A with the signed args from release B -- both signatures verify, but the combination was never intended.
  3. Strip. Depending on config, drop optional signed inputs (the actor controls what each URL returns), reverting to inline / default behavior. (Note: outright omitting a required args_url fetch fails closed; the concern is serving a stale-but-signed value.)

Scope

Applies to both stages: stage0 _stage1 (UKI + optional args) and stage1 _stage2 (payload + optional args). Threat actor = the endpoint/mirror controller, not a payload forger.

Not mitigated by PCR 14: measurement records what actually ran, so a relying party pinning an exact hash detects a rollback -- but the "trust the key, roll forward" model deliberately does not pin hashes, which is exactly where this bites.

Directions to consider (for later)

  • Sign a single manifest binding {payload hash, args hash, version/counter} so the pieces cannot be recombined and the version is authenticated.
  • A monotonic version / min-version in the signed data plus a persisted floor for anti-rollback (needs trusted storage, e.g. a TPM NV counter -- non-trivial).
  • Bind args to the payload (sign args together with the payload hash).

Related: the set_load_options silent-drop of a malformed signed directive (stage0 review finding #3) is part of this same args-integrity question.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions