Secret Vault on Aptos

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

Arbitrary Secret Disclosure via caller Parameter in get_secret

Root + Impact

Description

  • The get_secret function is intended to allow the vault owner to view the stored secret associated with their account. Under secure design, the caller of the transaction (transaction sender) should be verified before granting access to sensitive information.

  • Instead of checking that the transaction signer matches the vault owner, the function accepts an arbitrary caller: address parameter and compares it to the constant @owner. This means that any user can pass another user’s address (including the actual vault owner’s address) and retrieve their secret if the vault resource exists under that address. This bypasses signer authentication entirely and allows reading secrets without authorization.

#[view]
public fun get_secret (caller: address):String acquires Vault{
@> assert! (caller == @owner,NOT_OWNER); // @audit caller not tied to signer
let vault = borrow_global<Vault>(@owner);
vault.secret
}

Risk

Likelihood:

  • Any user with network access can craft a view call to get_secret with the vault owner’s address as the caller argument.

  • The issue is reachable with no preconditions beyond knowing a valid vault address.

Impact:

  • Secrets belonging to any vault owner can be disclosed without permission.

  • Breaks core confidentiality guarantees of the vault, undermining its entire purpose.

Proof of Concept

Add the following test to the secret_vault.move module.

It simulates an attacker supplying the owner’s address without having the owner signer.

The test should revert but it passed successfully, indicating the vulnerability.

#[test(owner = @0xcc, user = @0x123)]
fun test_others_can_read_owner_secret(owner: &signer, user: address) acquires Vault{
// Create a secret
let secret = b"i'm a secret";
set_secret(owner,secret);
// attacker (user) inputs the owner address who has secrets and reads it
get_secret(signer::address_of(owner));
debug::print(&b"Secret read successfully by attacker!");
}

Result:

Running Move unit tests
[debug] 0x5365637265742072656164207375636365737366756c6c792062792061747461636b657221
[ PASS ] 0x234::vault::test_others_can_read_owner_secret
[ PASS ] 0x234::vault::test_secret_vault
Test result: OK. Total tests: 2; passed: 2; failed: 0
{
"Result": "Success"
}

Recommended Mitigation

I would recommend removing the arbitrary caller parameter and deriving the address from the actual transaction signer using signer.

Note that the blockchain runtime automatically provides the signer of the transaction, so now no user can call get_secret and pass arbitrary data.

This ensures the requestor can only access their own vault data and prevents unauthorized secret disclosure.

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

Lead Judging Commences

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