Secret Vault

First Flight #46
Beginner FriendlyWallet
100 EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

Unrestricted Public Access to set_secret Enabling Arbitrary Users to Create Vaults

Root + Impact

Description

  • The set_secret function is designed as a public entry point for storing a secret in a Vault resource under the caller's account via move_to, emitting a SetNewSecret event. It relies on Move's signer authentication to ensure the resource is stored under the correct address, intending to support secure, owner-controlled storage as per the contract's role of limiting access to the owner.

  • The function lacks any access control checks (e.g., asserting the caller's address matches @owner), making it callable by any account with a signer, allowing unauthorized users to create their own Vaults and use the contract for unintended purposes. This violates the README's "only the owner" restriction, potentially leading to contract misuse, resource proliferation, or conflicts with the single-owner model, while exposing the contract to spam without mitigation.

public entry fun set_secret(caller:&signer,secret:vector<u8>){
// @> Root Cause Start: No access control assert (e.g., assert!(signer::address_of(caller) == @owner, NOT_OWNER);) to restrict callers.
let secret_vault = Vault{secret: string::utf8(secret)};
move_to(caller,secret_vault); // @> Allows storage under any caller's address, not just the owner.
event::emit(SetNewSecret {});
// @> Root Cause End: Public entry without checks enables arbitrary usage, contradicting owner-only intent.
}

Risk

Likelihood:

  • Any account with a signer invokes the public entry function set_secret to create a Vault under their address, as there are no ownership checks.

  • Deployment on public networks like Aptos testnet exposes the function to all users, enabling exploitation without barriers or costs beyond transaction fees.

Impact:

  • Arbitrary users create unintended Vault resources under their accounts, diluting the contract's purpose as a single-owner secure storage and potentially leading to resource bloat or misuse on-chain.

  • Malicious actors flood the contract with spurious calls, consuming storage and gas without restrictions, indirectly affecting the owner's usage or network performance.

Proof of Concept

This POC demonstrates that any unauthorized user can call set_secret to create a Vault under their own address, succeeding without restrictions. The test sets a secret for both the owner and an attacker, asserting that both resources exist afterward, proving the lack of ownership enforcement.

#[test(owner = @0xcc, attacker = @0x42)]
fun test_unrestricted_set_secret_access(owner: &signer, attacker: &signer) {
// 1. SETUP: Create accounts for owner and attacker
use aptos_framework::account;
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(attacker));
// 2. OWNER CALL: Sets a secret (expected behavior)
let owner_secret = b"owner's secret";
set_secret(owner, owner_secret);
let owner_addr = signer::address_of(owner);
// 3. EXPLOIT: Attacker (unauthorized) calls set_secret and succeeds
let attacker_secret = b"attacker's secret";
set_secret(attacker, attacker_secret); // Should fail if restricted, but succeeds due to no checks
let attacker_addr = signer::address_of(attacker);
// 4. ASSERT: Verify both resources exist, proving arbitrary access
assert!(exists<Vault>(owner_addr), 4); // Owner's Vault created
assert!(exists<Vault>(attacker_addr), 5); // Attacker's Vault created (vulnerability proof)
// 5. Debug print to confirm exploit success
debug::print(&b"--> POC Successful: Attacker created unauthorized Vault! <--");
}

Recommended Mitigation

Add an ownership check in set_secret to restrict calls to @owner only.

- 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>) {
+ assert!(signer::address_of(caller) == @owner, NOT_OWNER); // Restrict to owner only
+ let secret_vault = Vault { secret: string::utf8(secret) };
+ move_to(caller, secret_vault);
+ event::emit(SetNewSecret {});
+ }
Updates

Lead Judging Commences

bube Lead Judge 11 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

Anyone can call `set_secret` function

In Move for Aptos, the term "owner" refers to a signer, which is a verified account that owns a given resource, has permission to add resources and the ability to grant access or modify digital assets. Following this logic in this contest, the owner is the account that owns `Vault`. This means that anyone has right to call `set_secret` and then to own the `Vault` and to retrieve the secret from the `Vault` in `get_secret` function. Therefore, this group is invalid, because the expected behavior is anyone to call the `set_secret` function.

Support

FAQs

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