Secret Vault on Aptos

First Flight #46
Beginner FriendlyWallet
100 EXP
View results
Submission Details
Severity: low
Valid

Business logic in move_to, preventing the updation of secret

Root + Impact

Description

  • Normally, the vault owner should be able to rotate or update their secret multiple times to maintain security best practices and accommodate changing requirements.

  • The current implementation uses move_to directly, which aborts if a Vault resource already exists at the account address. This prevents any subsequent secret updates after the initial storage, creating a permanent lockout situation where the owner cannot modify, rotate, or correct their secret once it's been set.

public entry fun set_secret(caller: &signer, secret: vector<u8>) {
let secret_vault = Vault{secret: string::utf8(secret)};
@> move_to(caller, secret_vault); // Aborts if Vault already exists
event::emit(SetNewSecret {});
}

Risk

Likelihood:

  • This occurs immediately after the first successful secret storage operation when the owner attempts any subsequent update.

  • Every call to set_secret after the initial one will abort due to the existing resource constraint in Move's resource model.

Impact:

  • Owner cannot rotate, update, or replace their secret, violating security best practices for credential management.

  • Permanent denial of service for secret management functionality after first use.

  • System becomes unusable for real-world secret storage requirements where periodic rotation is essential.

Proof of Concept

// First call: succeeds and creates Vault resource
set_secret(owner_signer, b"secret1");
// Second call: aborts at move_to due to existing resource
set_secret(owner_signer, b"secret2"); // Transaction fails with resource already exists error

Recommended Mitigation

Check if a Vault resource already exists and update it in-place using borrow_global_mut, otherwise create a new one with move_to:

- public entry fun set_secret(caller: &signer, secret: vector<u8>) {
- let secret_vault = Vault{secret: string::utf8(secret)};
- move_to(caller, secret_vault);
- event::emit(SetNewSecret {});
- }
+ public entry fun set_secret(caller: &signer, secret: vector<u8>) {
+ if (exists<Vault>(signer::address_of(caller))) {
+ let v = borrow_global_mut<Vault>(signer::address_of(caller));
+ v.secret = string::utf8(secret);
+ } else {
+ move_to(caller, Vault{ secret: string::utf8(secret) });
+ }
+ event::emit(SetNewSecret {});
+ }

Ths enables safe secret rotation and prevents permanent lockout after the initial secret storage.

Updates

Lead Judging Commences

bube Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

The `secret` can not be updated

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!