Secret Vault on Aptos

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

Resource Existence Collision DoS on Secret Updates

# Resource Existence Collision DoS on Secret Updates

## Description

* The `set_secret()` function is intended to allow owners to set and update their vault secrets

* The function uses `move_to()` unconditionally, which will abort if a `Vault` resource already exists at the caller's address. After the first successful call to `set_secret()`, all subsequent calls will fail with a runtime error, permanently preventing secret updates or rotation

```java

public entry fun set_secret(caller: &signer, secret: vector<u8>) {

let secret_vault = Vault { secret: string::utf8(secret) };

move_to(caller, secret_vault); // @> This aborts if Vault already exists

event::emit(SetNewSecret {});

}

```

## Risk

**Likelihood**:

* Any second call to `set_secret()` by the same user will trigger an abort

* The issue occurs immediately after the first successful secret creation

* No workaround exists within the current contract design

* The problem is deterministic and affects all users who attempt to update their secrets

**Impact**:

* Complete denial of service for secret rotation functionality after first use

* Users cannot update compromised or outdated secrets

* Poor user experience with cryptic runtime errors on legitimate operations

* Security degradation as users cannot respond to potential secret compromises

* Business logic failure preventing normal application workflows

## Proof of Concept

* Add this test to `secret_vault.move` file and see the results

```java

#[test(owner = @0xcc)]

fun test_set_secret_dos(owner: &signer) {

use aptos_framework::account;

account::create_account_for_test(signer::address_of(owner));

let secret1 = b"first-secret";

let secret2 = b"rotated-secret";

// First set_secret call should succeed

set_secret(owner, secret1);

// This proves the DoS bug - second call will abort

set_secret(owner, secret2); // ABORT: Resource already exists

}

```

## Recommended Mitigation

* Here is the recommended mitigation

```diff

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 {});

+ let caller_address = signer::address_of(caller);

+ let secret_vault = Vault { secret: string::utf8(secret) };

+

+ // Check if vault already exists and handle accordingly

+ if (exists<Vault>(caller_address)) {

+ // Update existing vault

+ let vault_ref = borrow_global_mut<Vault>(caller_address);

+ vault_ref.secret = string::utf8(secret);

+ } else {

+ // Create new vault

+ move_to(caller, secret_vault);

+ };

+

+ event::emit(SetNewSecret {});

}

```

Updates

Lead Judging Commences

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

The `secret` can not be updated

Support

FAQs

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