The module’s core assumption is that only the owner can register new airdrop participants. However, get_random_slice
—the function responsible for inserting claimable amounts into the state—is declared as an entry
function, meaning anyone can call it directly.
Because of this, an attacker can skip the intended owner-only register_pizza_lover
flow and insert themselves (or arbitrary addresses they control) into the airdrop state. Once inserted, they can immediately claim funds.
Entry exposure: Marked as entry
, callable by any external signer.
No auth check: Lacks assert!(signer::address_of(caller) == state.owner, E_NOT_OWNER)
or equivalent.
State mutation: Directly modifies users_claimed_amount
, the central mapping of allocations.
Random allocation: Assigns 100–500
octas regardless of caller identity or intent.
Here, the owner must call register_pizza_lover
.
That in turn calls get_random_slice
.
But since get_random_slice
itself is public, this indirection is irrelevant.
Likelihood:
Developer intended get_random_slice
to be an internal helper but exposed it as callable.
Impact:
Loss of funds: Complete theft of the airdrop pool.
Bypass of business logic: Owner no longer controls who registers.
Attacker calls get_random_slice(attacker_addr)
.
→ Inserts attacker_addr → random_amount (100–500)
.
Attacker calls claim_pizza_slice(&attacker_signer)
.
→ Transfers that amount from resource account to attacker.
Repeat for multiple addresses.
→ Funds drained until state.balance == 0
.
Make get_random_slice
a private helper (remove entry
):
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.