Secret Vault on Aptos

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

Unauthorized Access and Improper Event Handling in secret_vault::vault Module Due to Undefined Owner in get_secret and Missing EventHandle in set_secret, Allowing Secret Exposure, Runtime Failures, and Broken dApp Auditability in Aptos Move Contracts

ROOT + IMPACT

ROOT CAUSE :

// Issue: get_secret referencing undefined owner
#[view]
public fun get_secret (caller: address):String acquires Vault{
assert! (caller == @owner,NOT_OWNER); // @> undefined owner
let vault = borrow_global(@owner);
vault.secret
}

// Issue: event emission without EventHandle in Vault
public entry fun set_secret(caller:&signer,secret:vector){
let secret_vault = Vault{secret: string::utf8(secret)};
move_to(caller,secret_vault);
event::emit(SetNewSecret {}); // @> EventHandle not created or referenced
}

Description

  • This will occur whenever get_secret is called, because the function references an undefined @owner instead of a verified signer or stored owner address, allowing unauthorized access attempts or runtime failures.

  • This will occur whenever set_secret is called, because the SetNewSecret event is emitted without creating or associating an EventHandle in the Vault, which can cause event emission to fail or be misattributed.

Impact:

  • Sensitive secret data stored in the Vault could be accessed by unauthorized addresses, leading to potential data leaks.

  • Event emission may fail or be incorrectly attributed, causing loss of auditability and breaking dApp functionality that relies on these events.

RISK :

  • Unauthorized access could expose sensitive secrets.

  • Events may fail or be misattributed, breaking dApp functionality and auditability.

  • Malicious actors could exploit this to manipulate or leak secret data.

Proof of Concept

// WARNING: Unauthorized access / event issues in secret_vault
// Calling get_secret with an arbitrary address may succeed or panic unexpectedly
// because ownership checks might be bypassed.
let secret = secret_vault::vault::get_secret(@0x123);
// Emitting an event without a proper EventHandle may fail or be misattributed.
// Always use a signer-linked EventHandle when setting secrets.
secret_vault::vault::set_secret(&some_signer, b"leak_secret");

Recommended Mitigation

- // problematic get_secret referencing undefined owner
- #[view]
- public fun get_secret (caller: address): String acquires Vault {
- assert!(caller == @owner, NOT_OWNER);
- let vault = borrow_global<Vault>(@owner);
- vault.secret
- }
- 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 {});
- }
+ // fixed Vault with EventHandle, owner-aware getters and proper event emission
+ struct Vault has key {
+ secret: String,
+ events: event::EventHandle<SetNewSecret>
+ }
+
+ public entry fun set_secret(caller: &signer, secret: vector<u8>) {
+ let secret_string = string::utf8(secret);
+ // create vault with an EventHandle tied to caller
+ let vault = Vault {
+ secret: secret_string,
+ events: event::new_event_handle<SetNewSecret>(caller)
+ };
+ // move the Vault into caller's account (overwrites existing vault if present)
+ move_to(caller, vault);
+ // emit using the stored EventHandle
+ let addr = signer::address_of(caller);
+ let vault_ref = borrow_global_mut<Vault>(addr);
+ event::emit_event(&mut vault_ref.events, SetNewSecret {});
+ }
+
+ #[view]
+ public fun get_secret(owner_addr: address): String acquires Vault {
+ // reads the Vault for the explicit owner address
+ let vault = borrow_global<Vault>(owner_addr);
+ vault.secret
+ }
+// `Vault` now includes an `EventHandle`, ensuring events are emitted correctly and attributed to the owner.
+//`get_secret` requires an explicit owner address, preventing unauthorized access and `set_secret` initializes the EventHandle with the signer, ensuring secure event logging.
Updates

Lead Judging Commences

bube Lead Judge 17 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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