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

Duplicate Records on Table and Data Integrity Vulnerability

Root + Impact

Description

  • The normal behavior is that the owner registers users for the pizza slice airdrop and assigns each user a claimable slice amount stored in a table. The user later claims this slice amount. Each user should have one assigned slice amount.

  • The specific issue is that the register_pizza_lover function uses table::add without checking if the user is already registered. Since table::add overwrites existing keys, a user can be registered multiple times, causing their slice amount to be overwritten or assigned multiple times. This bypasses intended uniqueness and can lead to abuse.

public entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
get_random_slice(user);
event::emit(PizzaLoverRegistered {
user: user,
}); @> // No check for existing registration, allows repeated registrations
}

Risk

Likelihood:

  • Whenever the owner calls register_pizza_lover for the same user multiple times, whether accidentally or maliciously.

  • This can occur during bulk registrations or due to lack of validation.

Impact:

  • A user can have their claimable slice amount overwritten or manipulated, potentially giving unfair advantages or causing accounting inconsistencies.

  • The contract’s internal state may become inconsistent, leading to confusion and potential loss of funds.

Proof of Concept

Re-register a user address multiple times via the register_pizza_lover entry, which overwrites the existing slice amount, resulting in unexpected claim amounts.

Recommended Mitigation

-public entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
- let state = borrow_global_mut<State>(get_resource_address());
- assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
- get_random_slice(user);
- event::emit(PizzaLoverRegistered {
- user: user,
- });
-}
+public entry fun register_pizza_lover(owner: &signer, user: address) acquires ModuleData, State {
+ let state = borrow_global_mut<State>(get_resource_address());
+ assert!(signer::address_of(owner) == state.owner, E_NOT_OWNER);
+ assert!(!table::contains(&state.users_claimed_amount, user), E_ALREADY_REGISTERED);
+ get_random_slice(user);
+ event::emit(PizzaLoverRegistered {
+ user: user,
+ });
+}
Updates

Appeal created

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

Support

FAQs

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