#43217 [BC-Insight] Incorrect public key notification after key rotation

Submitted on Apr 3rd 2025 at 19:05:25 UTC by @Rhaydden for Attackathon | Movement Labs

  • Report ID: #43217

  • Report Type: Blockchain/DLT

  • Report severity: Insight

  • Target: https://github.com/immunefi-team/attackathon-movement/tree/main/util/signing/signing-admin

  • Impacts:

    • A bug in the respective layer 0/1/2 network code that results in unintended smart contract behavior with no concrete funds at direct risk

Description

Brief/Intro

The signing-admin CLI tool has a flaw in its key rotation. Afterr rotating a signing key in either AWS KMS or HashiCorp Vault, the app is notified with the public key of the old key instead of the newly rotated key. This happens because the key retrieves the public key before the rotation operation has actually taken place from the perspective of the SignerBackend.

Vulnerability Details

In the rotate_key function in rotate_key.rs, the function's execution flow is as follows:

  1. An HttpApplication instance is created for notification.

  2. A Backend enum instance (representing the key storage backend) is created.

  3. A SignerBackend enum instance (used to retrieve the public key) is created. This creation happens before the actual key rotation. The SignerBackend is initialized with the canonical_string which identifies the key to be rotated.

    let signer = match backend_name.as_str() {
        "vault" => {
            // ... Vault client creation ...
            SignerBackend::Vault(HashiCorpVault::<Ed25519>::new(
                client,
                canonical_string.clone(),
                "transit".to_string(),
            ))
        }
        "aws" => {
            // ... AWS client creation ...
            SignerBackend::Aws(AwsKms::<Secp256k1>::new(
                client,
                canonical_string.clone(),
            ))
        }
        _ => return Err(anyhow::anyhow!("Unsupported signer backend: {}", backend_name)),
    };
  4. A KeyManager is created, which is responsible for performing the key rotation using the Backend.

  5. The key_manager.rotate_key() function is called. This function interacts with either AWS KMS or HashiCorp Vault to rotate the key associated with the canonical_string.

    key_manager
        .rotate_key(&canonical_string)
        .await
        .context("Failed to rotate the key")?;
  6. The public_key() method is called on the signer instance that was created in step 3. Since this instance was created before the rotation, it is still associated with the old key material.

    let public_key = signer
        .public_key()
        .await
        .context("Failed to fetch the public key from signer")?;
  7. The application.notify_public_key() function is called with the public_key obtained in step 6, which is the public key of the old key.

    key_manager
        .application
        .notify_public_key(public_key)
        .await
        .context("Failed to notify the application with the public key")?;

As a result, applications will be configured to verify signatures with the wrong key.

Impact Details

Apps relying on the key rotation notification mechanism will receive the incorrect public key. If the application uses the notified public key to verify signatures generated by the new key, the verification will fail because the notified public key corresponds to the old key. In scenarios where the public key is used for authentication purposes, the application will not be able to authenticate entities using the new key.

This falls under the category of "A bug in the respective layer 0/1/2 network code that results in unintended smart contract behavior with no concrete funds at direct risk" since it affects the signature verification without directly exposing funds to theft.

References

https://github.com/immunefi-team/attackathon-movement//blob/a2790c6ac17b7cf02a69aea172c2b38d2be8ce00/util/signing/signing-admin/src/cli/rotate_key.rs#L37-L106

Proof of Concept

  1. Set up the signing-admin with either AWS KMS or HashiCorp Vault backend

  2. Configure an application to receive public key updates

  3. Run the key rotation command:

signing-admin rotate-key --canonical-string "my-key" --application-url "http://my-app/api" --backend "aws"
  1. Verify that the application receives a public key

  2. Inspect the AWS KMS or Vault to confirm that key rotation has occurred

  3. Compare the public key sent to the application with the current public key in the backend

  4. The keys will not match.

Fix

Move the SignerBackend creation code to after the key_manager.rotate_key() call. This ensures that the signer is created with the new key, and applications are notified with the correct public key.

Was this helpful?