Secret Vault

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

Flawed Access Control Renders get_secret Unusable

Root + Impact

Description

  • The get_secret view function is intended to allow a user to read their stored secret.

  • The function's implementation contains flawed logic that makes it impossible to use correctly. It first asserts that the caller is the contract owner (@owner), and then it attempts to borrow the Vault resource from the @owner's address. However, the set_secret function stores the Vault at the caller's address, not the owner's. This mismatch means no user can ever successfully retrieve a secret.

// Root cause in the codebase with @> marks to highlight the relevant section
@> #[view]
public fun get_secret (caller: address):String acquires Vault{
assert! (caller == @owner,NOT_OWNER);
let vault = borrow_global<Vault >(@owner);
vault.secret
}@

Risk

Likelihood:

  • Any non-owner calling get_secret will always have their transaction reverted by the assert! check.

  • The contract owner calling get_secret will cause an abort due to a "resource not found" error, unless the owner has also called set_secret for themselves. The function can never read another user's Vault.

Impact:

  • The function to read data is completely non-functional, breaking the core logic of the contract.

  • Users can store secrets but have no way to view them, rendering the contract useless.

Proof of Concept

The following test demonstrates that neither a user nor the owner can retrieve the user's secret.

  1. A user creates a Vault by calling set_secret.

  2. An attempt by the user to call get_secret will always fail the assert!(caller == @owner) check, as shown in the test_user_cannot_get_secret which expects a failure with error code 1 (NOT_OWNER).

  3. An attempt by the owner to call get_secret to retrieve the user's secret will also fail, because the borrow_global<Vault>(@owner) call looks for a Vault at the owner's address, but it is stored at the user's address. This is shown in the test_owner_cannot_get_secret which will abort because the resource is missing.

#[test(owner = @0xcc, user = @0x123, expected_failure_code = 1)]
fun test_user_cannot_get_secret(owner: &signer, user: &signer) acquires Vault {
use aptos_framework::account;
// Setup
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(user));
// User sets a secret
set_secret(user, b"user secret");
// User tries to get the secret, but the call will fail because caller != @owner
get_secret(signer::address_of(user));
}
#[test(owner = @0xcc, user = @0x123, expected_failure_code = 0x10001)] // Not found error
fun test_owner_cannot_get_secret(owner: &signer, user: &signer) acquires Vault {
use aptos_framework::account;
// Setup
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(user));
// User sets a secret
set_secret(user, b"user secret");
// Owner tries to get the user's secret, but will fail because borrow_global looks at @owner's address
get_secret(signer::address_of(owner));
}

Recommended Mitigation

The function should be redesigned to allow a user to view the secret stored in their own Vault. The access control should check that the caller is the owner of the Vault they are trying to access.

This is accomplished by removing the incorrect check against the contract @owner and instead borrowing the Vault from the caller's own address. This ensures that any user can view their own secret, restoring the function's intended behavior.

- public fun get_secret (caller: address):String acquires Vault{
- assert! (caller == @owner,NOT_OWNER);
- let vault = borrow_global<Vault >(@owner);
-
- vault.secret
- }
+ public fun get_secret (caller: address):String acquires Vault{
+ // Let anyone read the secret of the vault they are querying
+ let vault = borrow_global<Vault >(caller);
+
+ vault.secret
+ }
Updates

Lead Judging Commences

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