#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:
An
HttpApplication
instance is created for notification.A
Backend
enum instance (representing the key storage backend) is created.A
SignerBackend
enum instance (used to retrieve the public key) is created. This creation happens before the actual key rotation. TheSignerBackend
is initialized with thecanonical_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)), };
A
KeyManager
is created, which is responsible for performing the key rotation using theBackend
.The
key_manager.rotate_key()
function is called. This function interacts with either AWS KMS or HashiCorp Vault to rotate the key associated with thecanonical_string
.key_manager .rotate_key(&canonical_string) .await .context("Failed to rotate the key")?;
The
public_key()
method is called on thesigner
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")?;
The
application.notify_public_key()
function is called with thepublic_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
Set up the signing-admin with either AWS KMS or HashiCorp Vault backend
Configure an application to receive public key updates
Run the key rotation command:
signing-admin rotate-key --canonical-string "my-key" --application-url "http://my-app/api" --backend "aws"
Verify that the application receives a public key
Inspect the AWS KMS or Vault to confirm that key rotation has occurred
Compare the public key sent to the application with the current public key in the backend
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?