get_random_slice is declared entry — any account can call it in a transaction.
get_random_slice computes random_amount and then does table::add(&mut state.users_claimed_amount, user_addr, random_amount); without checking if the key already exists.
register_pizza_lover is intended as owner-only registration, but it simply calls get_random_slice(user). Because get_random_slice is public, it can be executed by anyone (not necessarily the owner), before or instead of the owner.
table::add will abort if the key exists; it does not overwrite.
Likelihood:
get_random_slice is a public entry with no access control. No special privileges or complex conditions are required. Any account can call it and then call claim_pizza_slice.
Impact:
Full drain of the airdrop pool balance to attacker-controlled addresses
Self-registration & immediate drain (direct theft)
Attacker calls get_random_slice(attacker_address) (public entry). This inserts (attacker_address → random_amount) into users_claimed_amount.
Attacker calls claim_pizza_slice(&attacker_signer):
claim_pizza_slice checks table::contains(&state.users_claimed_amount, attacker_addr) → true.
claimed_users doesn't contain attacker_addr → true.
state.balance >= amount → if contract is funded, true.
The code registers account for AptosCoin if needed, calls transfer_from_contract(attacker_addr, amount) and marks claimed_users.
Result: attacker receives APT from the resource account; owner control is bypassed.
Remove public access to get_random_slice. Make it a module-internal function (remove entry) and call only from register_pizza_lover which already requires state.owner
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.