Secret Vault on Aptos

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

Missing Resource Existence Check in set_secret()

Root + Impact

Description

The set_secret() function is restricted to the contract owner and is intended to allow the owner to initialize or update the stored secret. However, the function calls move_to(caller, secret_vault) without checking if a Vault resource already exists at the owner’s address.

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 {});
}

In Move semantics, move_to aborts if the resource already exists. This means that once the owner sets the secret for the first time, any subsequent attempt to update it will always fail, effectively locking the secret permanently unless the resource is manually removed through other means.

Risk

Likelihood: High

  • Will occur every time the owner tries to update the secret after the initial setup.

Impact: High

  • Owner loses the ability to rotate or update the secret.

Proof of Concept

The following PoC, which can be added to the secret_vault::vault module, demonstrates that the contract owner becomes permanently unable to update the stored secret after the initial creation due to the lack of a resource existence check in set_secret().

/// [VULNERABILITY PoC] Resource Re-initialization DoS
/// The `set_secret()` function lacks duplicate check, causing panic on re-initialization
#[test(owner = @0xcc, user = @0x123)]
#[expected_failure]
fun test_dos_set_secret(owner: &signer, user: &signer) {
use aptos_framework::account;
use std::string::utf8;
// Setup: Initialize test accounts
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(user));
// Step 1: First secret creation (should succeed)
let secret = b"i'm a secret";
set_secret(owner, secret);
debug::print(&utf8(b"[SUCCESS] First secret set successfully"));
// Step 2: Attempt to update secret (will fail due to resource already existing)
debug::print(&utf8(b"[WARNING] Attempting to update secret - this will fail..."));
let new_secret = b"i'm a new secret";
set_secret(owner, new_secret); // Expected to panic here
}

Execution Output:

/// [VULNERABILITY PoC] Resource Re-initialization DoS
/// The `set_secret()` function lacks duplicate check, causing panic on re-initialization
#[test(owner = @0xcc, user = @0x123)]
#[expected_failure]
fun test_dos_set_secret(owner: &signer, user: &signer) {
use aptos_framework::account;
use std::string::utf8;
// Setup: Initialize test accounts
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(user));
// Step 1: First secret creation (should succeed)
let secret = b"i'm a secret";
set_secret(owner, secret);
debug::print(&utf8(b"[SUCCESS] First secret set successfully"));
// Step 2: Attempt to update secret (will fail due to resource already existing)
debug::print(&utf8(b"[WARNING] Attempting to update secret - this will fail..."));
let new_secret = b"i'm a new secret";
set_secret(owner, new_secret); // Expected to panic here
}

Recommended Mitigation

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

Lead Judging Commences

bube Lead Judge 16 days 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.