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 4 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!