One Shot: Reloaded

First Flight #47
Beginner FriendlyNFT
100 EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

Staking rewards are destroyed if the user's account is not pre-registered for CRED.

Root + Impact

Description

  • The normal behavior of the cred_token module is to mint and deposit CRED tokens to a user's account. The specific issue is that the mint function checks if the recipient's account is already registered for the CRED token. If the account is not registered, the function calls coin::destroy_zero, which permanently destroys the minted coins. The user receives no error or notification that their rewards were lost.

// Root cause in the codebase with @> marks to highlight the relevant section
public(friend) fun mint(
module_owner: &signer,
to: address,
amount: u64
) acquires CredCapabilities {
let caps = borrow_global<CredCapabilities>(signer::address_of(module_owner));
let coins = coin::mint<CRED>(amount, &caps.mint_cap);
if (coin::is_account_registered<CRED>(to)) { @>
coin::deposit(to, coins);
} else {
coin::destroy_zero(coins); @>
};
}

Risk

Likelihood:

  • This vulnerability will occur whenever a user attempts to unstake a Rapper and they have not yet registered their account for the CRED token.

  • This is a common pattern in Aptos, and users may not be aware they need to call the register function first.

Impact:

  • While not a critical security vulnerability, this creates a poor user experience.

  • Users will believe they have received their staking rewards when in fact they have been destroyed, which could lead to confusion and loss of trust.

Proof of Concept

public(friend) fun mint(
module_owner: &signer,
to: address,
amount: u64
) acquires CredCapabilities {
let caps = borrow_global<CredCapabilities>(signer::address_of(module_owner));
let coins = coin::mint<CRED>(amount, &caps.mint_cap);
if (coin::is_account_registered<CRED>(to)) { @>
coin::deposit(to, coins);
} else {
coin::destroy_zero(coins); @>
};
}

Recommended Mitigation

- if (coin::is_account_registered<CRED>(to)) {
- coin::deposit(to, coins);
- } else {
- coin::destroy_zero(coins);
- };
+ assert!(coin::is_account_registered<CRED>(to), E_ACCOUNT_NOT_REGISTERED);
+ coin::deposit(to, coins);
Updates

Lead Judging Commences

bube Lead Judge 17 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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