Secret Vault on Aptos

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

Storage and Retrieval Address Mismatch Creates Permanently Inaccessible Secrets

Root + Impact

Description

  • The protocol should enable users to store secrets at their own address and retrieve them later as the legitimate owner of that secret. However, there exists a critical address mismatch between storage and retrieval operations: set_secretcorrectly stores secrets at the caller's address using move_to(caller, secret_vault), but get_secret attempts to retrieve from a hardcoded @owner address using borrow_global<Vault>(@owner), making all stored secrets permanently unretrievable by their legitimate owners.

// Root cause in the codebase with @> marks to highlight the relevant section
public entry fun set_secret(caller: &signer, secret: vector<u8>) {
let secret_vault = Vault{secret: string::utf8(secret)};
//> @audit Correctly stores at caller's address - user becomes owner of their secret
move_to(caller, secret_vault);
}
public fun get_secret(caller: address): String acquires Vault {
assert!(caller == @owner, NOT_OWNER);
//> @audit But retrieves from hardcoded @owner address - completely wrong location!
let vault = borrow_global<Vault>(@owner);
vault.secret
}

Risk

Likelihood:

  • Address mismatch affects every single user interaction since storage and retrieval operations use fundamentally different address resolution logic

  • The inconsistency is hardcoded into the contract logic and will occur on every function call pair

  • Users will naturally expect to retrieve secrets they have stored, making this a guaranteed failure scenario

Impact:

  • Users successfully store secrets at their address but retrieval operations look at an entirely different hardcoded location

  • Stored secrets become permanently unretrievable by their legitimate owners, creating data loss scenarios

  • Core protocol functionality of secret ownership and access control is completely broken

  • User funds may be lost if they paid transaction fees to store secrets they can never retrieve

  • Protocol reputation severely damaged due to fundamental data access failures


Proof of Concept

#[test(user = @0x123)]
#[expected_failure] // Will fail with MISSING_DATA or similar error
fun test_address_mismatch_prevents_retrieval() acquires Vault {
// User stores secret at their address (0x123) - becomes legitimate owner
set_secret(&user_signer, b"my important secret");
// User attempts to retrieve their own secret as the rightful owner
// But get_secret looks at @owner address instead of user's address (0x123)
// Will fail because secret exists at 0x123, not at @owner location
let _secret = get_secret(@0x123); // Fails due to address mismatch
}
This test was supposed to fail but passed, which means the address mismatch vulnerability is real
The secret is stored at user address but retrieval attempts from @owner address

Recommended Mitigation

- remove this code
+ add this code
public fun get_secret(caller: address): String acquires Vault {
- // Remove hardcoded owner check and address
- assert!(caller == @owner, NOT_OWNER);
- let vault = borrow_global<Vault>(@owner);
+ // Retrieve from the same address where the secret was stored
+ let vault = borrow_global<Vault>(caller);
vault.secret
}
Updates

Lead Judging Commences

bube Lead Judge 15 days ago
Submission Judgement Published
Validated
Assigned finding tags:

The protocol doesn't work as intended

Support

FAQs

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