Beginner FriendlyGameFi
100 EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Multiple Claims Possible After Re-Registration

Root + Impact

Description

  • Normally, each registered user should be able to claim their pizza slice only once. After claiming, their slice entry is removed from the table, preventing further claims.

  • However, because the slice assignment (get_random_slice) can be called again on an already-claimed address, the user’s entry is reinserted into the table. This allows malicious users (or mistakes by the owner) to give the same account multiple slices, breaking the “one slice per person” rule.

public entry fun get_random_slice(account: address) {
let random_amount = 100 + (timestamp::now_microseconds() % 401);
@> table::add(&mut pizza_slices, account, random_amount);
// User can be re-added after already claiming
}

Risk

Likelihood:

  • This occurs whenever the same user is re-registered after claiming.

  • Because get_random_slice is an entry function, any user may directly call it themselves, bypassing owner control.

Impact:

  • A malicious user can claim multiple slices of APT, draining the pool.

  • The “one slice per person” fairness guarantee is broken.

Proof of Concept

  • A user can re-register after claiming to bypass the “one slice per person” rule and drain multiple rewards from the pool.

// Attacker registers and claims once
airdrop::get_random_slice(@0xAttacker);
airdrop::claim_pizza_slice(@0xAttacker);
// Attacker re-registers (self-calls get_random_slice again)
airdrop::get_random_slice(@0xAttacker);
// Attacker claims a second time
airdrop::claim_pizza_slice(@0xAttacker);
// Pool drained by the same address multiple times

Recommended Mitigation

  • Restrict registration to the contract owner and enforce a check preventing re-registration after a claim.

- public entry fun get_random_slice(account: address) {
- let random_amount = 100 + (timestamp::now_microseconds() % 401);
- table::add(&mut pizza_slices, account, random_amount);
- }
+ public(entry) fun get_random_slice(owner: &signer, account: address) {
+ assert!(signer::address_of(owner) == @PizzaDropAdmin, E_NOT_OWNER);
+ assert!(!table::contains(&pizza_slices, account), E_ALREADY_REGISTERED);
+ let rand = aptos_framework::randomness::generate_u64(&Randomness);
+ let random_amount = 100 + (rand % 401);
+ table::add(&mut pizza_slices, account, random_amount);
+ }
Updates

Appeal created

bube Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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