Beginner FriendlyGameFi
100 EXP
View results
Submission Details
Severity: medium
Valid

Predictable Randomness via timestamp

Weak and Predictable Randomness can cause unfair allocation of airdrop to users.

Description

  • The airdrop amount depends on timestamp::now_microseconds(), which is deterministic and predictable.

  • Attackers can time their registration transactions to maximize their rewards.

  • An attacker can calculate the exact timestamp and predict the amount they'll receive, potentially timing their registration to get maximum allocation (500).

#[randomness]
entry fun get_random_slice(user_addr: address) acquires ModuleData, State {
let state = borrow_global_mut<State>(get_resource_address());
let time = timestamp::now_microseconds();
let random_val = time % 401;
let random_amount = 100 + random_val; // 100-500 APT (in Octas: 10^8 smallest unit)
table::add(&mut state.users_claimed_amount, user_addr, random_amount);
}

Risk

Likelihood:

  • Every time a user registers, their reward is derived from timestamp.

  • Users can front-run or retry transactions until they receive high allocations.

Impact:

  • Creates unfair distribution of airdrops.

  • Wealthy/technical participants can bias results in their favor, undermining fairness of the drop.

Proof of Concept

So, instead of using a modulus function with timestamp, we should instead focus on using Oracles to generate a random number for allocation to be done fairly.
In the current scenario, any user can register by noting the timestamp condition and registering accordingly.

Let's say :
A user gets in at timestamp 801, then 801%401 will be 400 and +100 to that will make it 500.
Now, the next user will wait for the timestamp to go to 1202, so that 1202%401 will return 400 and +100 to that equals 500. So the users might misuse it to register at timestamps which are 1 less than multiples of 401 to gain maximum value with modulus function and hence greater allocation.

// A user calls register_pizza_lover repeatedly with controlled timing.
// By monitoring timestamp % 401, they can ensure they hit high-value slices (close to 500 APT).
// User
let random_amount = verified_randomness_between(100,500);

Recommended Mitigation

Using a randomised function to pick values between 100-500, it will ensure that allocation is done extremely randomly instead of a fixed timestamp based allocation which will increase fairness to all and also maintain randomisability.

- let time = timestamp::now_microseconds();
- let random_val = time % 401;
- let random_amount = 100 + random_val;
// Use a VRF or off-chain randomness oracle
+ let random_amount = verified_randomness_between(100, 500);
Updates

Appeal created

bube Lead Judge 12 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Predictable randomness

The `get_random_slice` function should only be called by the owner via the `register_pizza_lover` function. Also, the `owner` is trusted and will not choose a specific time for a new user to register. Therefore, I disagree with the claim of most reports in this group that an attacker can manipulate the random number of pizza slices. But I agree with the root cause of the reports in this group, that the random distribution is not completely random.

Support

FAQs

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